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

mattwparas / steel / 17772690385

16 Sep 2025 04:36PM UTC coverage: 43.331% (+0.04%) from 43.289%
17772690385

Pull #519

github

web-flow
Merge 98d4fd22c into 3c10433b9
Pull Request #519: fix a bunch more clippy lints

56 of 123 new or added lines in 30 files covered. (45.53%)

8 existing lines in 3 files now uncovered.

12416 of 28654 relevant lines covered (43.33%)

2985398.75 hits per line

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

40.99
/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::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), usize>,
14

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

18
    depth: usize,
19

20
    external: bool,
21
}
22

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

30
impl CycleDetector {
31
    pub(super) fn detect_and_display_cycles(
12,461✔
32
        val: &SteelVal,
33
        f: &mut fmt::Formatter,
34
        external: bool,
35
    ) -> fmt::Result {
36
        // Consider using one shared queue here
37
        let mut queue = Vec::new();
24,922✔
38

39
        let mut bfs_detector = CycleCollector {
40
            visited: fxhash::FxHashSet::default(),
24,922✔
41
            cycles: fxhash::FxHashMap::default(),
24,922✔
42
            values: Vec::new(),
12,461✔
43
            queue: &mut queue,
12,461✔
44
            found_mutable: false,
45
        };
46

47
        bfs_detector.push_back(val.clone());
49,844✔
48

49
        bfs_detector.visit();
24,922✔
50

51
        CycleDetector {
52
            cycles: bfs_detector.cycles,
24,922✔
53
            values: bfs_detector.values,
12,461✔
54
            depth: 0,
55
            external,
56
        }
57
        .start_format(val, f)
37,383✔
58
    }
59

60
    fn start_format(mut self, val: &SteelVal, f: &mut fmt::Formatter) -> fmt::Result {
12,461✔
61
        for node in std::mem::take(&mut self.values) {
24,922✔
62
            let id = match &node {
×
63
                SteelVal::CustomStruct(c) => {
×
64
                    let ptr_addr = c.as_ptr() as usize;
×
65
                    self.cycles.get(&(ptr_addr, 0)).unwrap()
×
66
                }
67
                SteelVal::HeapAllocated(b) => {
×
68
                    // Get the object that THIS points to
69
                    let ptr_addr = b.get().as_ptr_usize().unwrap();
×
70
                    self.cycles.get(&(ptr_addr, 0)).unwrap()
×
71
                }
72
                SteelVal::ListV(l) => {
×
73
                    let ptr_addr = l.identity_tuple();
×
74
                    self.cycles.get(&ptr_addr).unwrap()
×
75
                }
76
                SteelVal::VectorV(l) => {
×
77
                    let ptr_addr = (l.0.as_ptr() as usize, 0);
×
78

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

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

89
                    self.cycles.get(&ptr_addr).unwrap()
×
90
                }
91
                SteelVal::Custom(l) => {
×
92
                    let ptr_addr = (l.as_ptr() as usize, 0);
×
93

94
                    self.cycles.get(&ptr_addr).unwrap()
×
95
                }
96
                SteelVal::Boxed(b) => {
×
97
                    let ptr_addr = (b.as_ptr() as usize, 0);
×
98

99
                    self.cycles.get(&ptr_addr).unwrap()
×
100
                }
101
                SteelVal::SyntaxObject(s) => {
×
102
                    let ptr_addr = (s.as_ptr() as usize, 0);
×
103

104
                    self.cycles.get(&ptr_addr).unwrap()
×
105
                }
106
                SteelVal::MutableVector(v) => {
×
107
                    let ptr_addr = (v.as_ptr_usize(), 0);
×
108

109
                    self.cycles.get(&ptr_addr).unwrap()
×
110
                }
111
                SteelVal::Pair(p) => {
×
112
                    let ptr_addr = (p.as_ptr() as usize, 0);
×
113
                    self.cycles.get(&ptr_addr).unwrap()
×
114
                }
115
                _ => {
116
                    unreachable!()
117
                }
118
            };
119

120
            write!(f, "#{id}=")?;
×
121
            self.format_with_cycles(&node, f, FormatType::TopLevel)?;
×
122
            writeln!(f)?;
×
123
        }
124

125
        if !self.values.contains(val) {
12,461✔
126
            self.format_with_cycles(val, f, FormatType::Normal)?;
12,461✔
127
        }
128

129
        Ok(())
12,461✔
130
    }
131

132
    fn format_with_cycles(
12,919✔
133
        &mut self,
134
        val: &SteelVal,
135
        f: &mut fmt::Formatter,
136
        format_type: FormatType,
137
    ) -> fmt::Result {
138
        self.depth += 1;
12,919✔
139

140
        if self.depth > 128 {
12,919✔
141
            self.depth -= 1;
×
142
            return write!(f, "...");
×
143
        }
144

145
        let res = match val {
14,346✔
146
            BoolV(b) => write!(f, "#{b}"),
364✔
147
            NumV(x) => write!(f, "{}", RealLiteral::Float(*x)),
335✔
148
            IntV(x) => write!(f, "{x}"),
2,960✔
149
            BigNum(b) => write!(f, "{}", b.as_ref()),
×
150
            Rational(x) => write!(f, "{n}/{d}", n = x.numer(), d = x.denom()),
56✔
151
            BigRational(x) => write!(f, "{n}/{d}", n = x.numer(), d = x.denom()),
12✔
152
            Complex(x) => write!(f, "{}", x.as_ref()),
×
153
            StringV(s) if self.external => write!(f, "{s:?}"),
10,495✔
154
            StringV(s) => write!(f, "{s}"),
10,025✔
155
            ByteVector(b) => {
×
156
                write!(f, "#u8(")?;
×
157

158
                let bytes = b.vec.read();
×
159
                let mut iter = bytes.iter();
160

161
                if let Some(last) = iter.next_back() {
×
162
                    for byte in iter {
×
163
                        write!(f, "#x{byte:02X} ")?;
×
164
                    }
165

166
                    write!(f, "#x{last:02X}")?;
×
167
                }
168

169
                write!(f, ")")
×
170
            }
171
            CharV(c) if self.external => match c {
1,439✔
172
                ' ' => write!(f, "#\\space"),
3✔
173
                '\0' => write!(f, "#\\null"),
×
174
                '\t' => write!(f, "#\\tab"),
×
175
                '\n' => write!(f, "#\\newline"),
×
176
                '\r' => write!(f, "#\\return"),
×
177
                _ => {
178
                    let escape = c.escape_debug();
9✔
179
                    if escape.len() <= 2 {
3✔
180
                        // char does not need escaping
181
                        write!(f, "#\\{}", c)
9✔
182
                    } else {
183
                        // escape char as #\uNNNN
184
                        write!(f, "#\\u{:04x}", *c as u32)
×
185
                    }
186
                }
187
            },
188
            CharV(c) => write!(f, "{c}"),
1,421✔
189
            Pair(p) => {
12✔
190
                if let Some(value) = self.cycles.get(&(p.as_ptr() as usize, 0)) {
36✔
191
                    write!(f, "#{}#", value)
192
                } else {
193
                    write!(f, "(")?;
36✔
194
                    self.format_with_cycles(&p.car, f, FormatType::Normal)?;
12✔
195
                    write!(f, " . ")?;
12✔
196
                    self.format_with_cycles(&p.cdr, f, FormatType::Normal)?;
12✔
197
                    write!(f, ")")
12✔
198
                }
199
            }
200
            FuncV(func) => {
6✔
201
                if let Some(name) = get_function_name(*func) {
12✔
202
                    write!(f, "#<function:{}>", name.name)
203
                } else {
204
                    write!(f, "#<function>")
×
205
                }
206
            }
207
            Void => write!(f, "#<void>"),
81✔
208
            SymbolV(s) => write!(f, "{s}"),
848✔
209
            VectorV(lst) => {
×
210
                let mut iter = lst.iter();
×
211
                write!(f, "#(")?;
×
212

213
                if let Some(last) = iter.next_back() {
×
214
                    for item in iter {
×
215
                        self.format_with_cycles(item, f, FormatType::Normal)?;
×
216
                        write!(f, " ")?;
×
217
                    }
218
                    self.format_with_cycles(last, f, FormatType::Normal)?;
×
219
                }
220
                write!(f, ")")
×
221
            }
222
            // TODO: Somehow getting an already borrowed error here on 208
223
            Custom(x) => match format_type {
×
224
                FormatType::Normal => write!(
×
225
                    f,
×
226
                    "{}",
227
                    x.try_read()
228
                        .map(|x| x.display())
×
229
                        .unwrap_or(Ok(format!("#<{:p}>", x)))?
×
230
                ),
231
                FormatType::TopLevel => write!(f, "#<{}>", x.read().display()?),
×
232
            },
233
            CustomStruct(s) => match format_type {
8✔
234
                FormatType::Normal => {
235
                    if let Some(id) = self.cycles.get(&(s.as_ptr() as usize, 0)) {
4✔
236
                        write!(f, "#{id}#")
237
                    } else {
238
                        let guard = s;
8✔
239

240
                        {
241
                            if s.get(&SteelVal::SymbolV(SteelString::from("#:transparent")))
8✔
242
                                .and_then(|x| x.as_bool())
12✔
243
                                .unwrap_or_default()
244
                            {
245
                                write!(f, "({}", guard.name())?;
4✔
246

247
                                for i in guard.fields.iter() {
4✔
248
                                    write!(f, " ")?;
9✔
249
                                    self.format_with_cycles(i, f, FormatType::Normal)?;
3✔
250
                                }
251

252
                                write!(f, ")")
1✔
253
                            } else {
254
                                write!(f, "({})", guard.name())
3✔
255
                            }
256
                        }
257
                    }
258
                }
259
                FormatType::TopLevel => {
260
                    let guard = s;
×
261
                    {
262
                        if guard
×
263
                            .get(&SteelVal::SymbolV(SteelString::from("#:transparent")))
×
264
                            .and_then(|x| x.as_bool())
×
265
                            .unwrap_or_default()
266
                        {
267
                            write!(f, "({}", guard.name())?;
×
268

269
                            for i in guard.fields.iter() {
×
270
                                write!(f, " ")?;
×
271
                                self.format_with_cycles(i, f, FormatType::Normal)?;
×
272
                            }
273

274
                            write!(f, ")")
×
275
                        } else {
276
                            write!(f, "({})", guard.name())
×
277
                        }
278
                    }
279
                }
280
            },
281
            PortV(port) => write!(f, "{}", port),
×
282
            Closure(_) => write!(f, "#<bytecode-closure>"),
17✔
283
            HashMapV(hm) => write!(f, "#<hashmap {:#?}>", hm.as_ref()),
5✔
284
            IterV(_) => write!(f, "#<iterator>"),
×
285
            HashSetV(hs) => write!(f, "#<hashset {:?}>", hs.0),
×
286
            FutureFunc(_) => write!(f, "#<future-func>"),
×
287
            FutureV(_) => write!(f, "#<future>"),
×
288
            StreamV(_) => write!(f, "#<stream>"),
18✔
289
            BoxedFunction(b) => {
×
290
                if let Some(name) = b.name() {
×
291
                    write!(f, "#<function:{}>", name)
292
                } else {
293
                    write!(f, "#<function>")
×
294
                }
295
            }
296
            ContinuationFunction(_) => write!(f, "#<continuation>"),
×
297
            // #[cfg(feature = "jit")]
298
            // CompiledFunction(_) => write!(f, "#<compiled-function>"),
299
            ListV(l) => match format_type {
356✔
300
                FormatType::Normal => {
301
                    if let Some(value) = self.cycles.get(&l.identity_tuple()) {
178✔
302
                        write!(f, "#{}#", value)
303
                    } else {
304
                        write!(f, "(")?;
534✔
305

306
                        let mut iter = l.iter().peekable();
178✔
307

308
                        while let Some(item) = iter.next() {
1,038✔
309
                            self.format_with_cycles(item, f, FormatType::Normal)?;
×
310
                            if iter.peek().is_some() {
430✔
311
                                write!(f, " ")?
813✔
312
                            }
313
                        }
314
                        write!(f, ")")
534✔
315
                    }
316
                }
317
                FormatType::TopLevel => {
318
                    write!(f, "(")?;
×
319

320
                    let mut iter = l.iter().peekable();
×
321

322
                    while let Some(item) = iter.next() {
×
323
                        self.format_with_cycles(item, f, FormatType::Normal)?;
×
324
                        if iter.peek().is_some() {
×
325
                            write!(f, " ")?
×
326
                        }
327
                    }
328
                    write!(f, ")")
×
329
                }
330
            },
331
            // write!(f, "#<list {:?}>", l),
332
            MutFunc(_) => write!(f, "#<function>"),
×
333
            BuiltIn(_) => write!(f, "#<function>"),
×
334
            ReducerV(_) => write!(f, "#<reducer>"),
×
335
            MutableVector(v) => match format_type {
4✔
336
                FormatType::Normal => {
337
                    if let Some(value) = self.cycles.get(&(v.as_ptr_usize(), 0)) {
2✔
338
                        write!(f, "#{}#", value)
339
                    } else {
340
                        write!(f, "#(")?;
6✔
341
                        let guard = v.inner.upgrade().unwrap();
2✔
342
                        let guard = guard.read();
343

344
                        let mut iter = guard.value.iter().peekable();
345

346
                        while let Some(item) = iter.next() {
4✔
347
                            self.format_with_cycles(item, f, FormatType::Normal)?;
×
348
                            if iter.peek().is_some() {
1✔
349
                                write!(f, " ")?
×
350
                            }
351
                        }
352
                        write!(f, ")")
6✔
353
                    }
354
                }
355
                FormatType::TopLevel => {
356
                    write!(f, "#(")?;
×
357
                    let guard = v.inner.upgrade().unwrap();
×
358
                    let guard = guard.read();
359

360
                    let mut iter = guard.value.iter().peekable();
361

362
                    while let Some(item) = iter.next() {
×
363
                        self.format_with_cycles(item, f, FormatType::Normal)?;
×
364
                        if iter.peek().is_some() {
×
365
                            write!(f, " ")?
×
366
                        }
367
                    }
368
                    write!(f, ")")
×
369
                }
370
            },
371
            SyntaxObject(s) => {
1✔
372
                if let Some(raw) = &s.raw {
2✔
373
                    write!(f, "#<syntax:{:?} {:?}>", s.span, raw)
374
                } else {
375
                    write!(f, "#<syntax:{:?} {:?}>", s.span, s.syntax)
×
376
                }
377
            }
378
            BoxedIterator(_) => write!(f, "#<iterator>"),
×
379
            Boxed(b) => write!(f, "'#&{}", b.read()),
×
380
            Reference(x) => write!(f, "{}", x.format()?),
×
381
            HeapAllocated(b) => {
×
382
                let maybe_id = b
×
383
                    .get()
384
                    .as_ptr_usize()
385
                    .and_then(|x| self.cycles.get(&(x, 0)));
×
386
                match (maybe_id, format_type) {
×
387
                    (Some(id), FormatType::Normal) => {
×
388
                        write!(f, "#{id}#")
389
                    }
390
                    _ => {
391
                        write!(f, "'#&{}", b.get())
×
392
                    }
393
                }
394
            }
395
        };
396

397
        self.depth -= 1;
398

399
        res
400
    }
401
}
402

403
fn replace_with_void(value: &mut SteelVal) -> SteelVal {
4✔
404
    std::mem::replace(value, SteelVal::Void)
12✔
405
}
406

407
impl SteelVal {
408
    fn make_void(&mut self) -> SteelVal {
228✔
409
        std::mem::replace(self, SteelVal::Void)
684✔
410
    }
411
}
412

413
pub(crate) struct SteelCycleCollector {
414
    cycles: fxhash::FxHashMap<(usize, usize), usize>,
415
    values: List<SteelVal>,
416
}
417

418
impl Custom for SteelCycleCollector {}
419

420
impl SteelCycleCollector {
421
    pub fn from_root(value: SteelVal) -> Self {
10,959✔
422
        let mut queue = Vec::new();
21,918✔
423

424
        let mut collector = CycleCollector {
425
            visited: fxhash::FxHashSet::default(),
21,918✔
426
            cycles: fxhash::FxHashMap::default(),
21,918✔
427
            values: Vec::new(),
10,959✔
428
            queue: &mut queue,
10,959✔
429
            found_mutable: false,
430
        };
431

432
        collector.push_back(value);
32,877✔
433

434
        collector.visit();
21,918✔
435

436
        if collector.found_mutable {
10,959✔
437
            SteelCycleCollector {
438
                cycles: collector.cycles,
24✔
439
                values: collector.values.into(),
12✔
440
            }
441
        } else {
442
            SteelCycleCollector {
443
                cycles: fxhash::FxHashMap::default(),
10,947✔
444
                values: List::new(),
10,947✔
445
            }
446
        }
447
    }
448

449
    // Get the value
450
    pub fn get(&self, node: SteelVal) -> Option<usize> {
189✔
451
        match node {
189✔
452
            SteelVal::CustomStruct(c) => {
168✔
453
                let ptr_addr = (c.as_ptr() as usize, 0);
336✔
454
                self.cycles.get(&ptr_addr)
504✔
455
            }
456
            SteelVal::HeapAllocated(b) => {
×
457
                // Get the object that THIS points to
458
                let ptr_addr = (b.get().as_ptr_usize().unwrap(), 0);
×
459
                self.cycles.get(&ptr_addr)
×
460
            }
461
            SteelVal::MutableVector(v) => {
×
462
                let ptr_addr = (v.as_ptr_usize(), 0);
×
463
                self.cycles.get(&ptr_addr)
×
464
            }
465
            SteelVal::Pair(p) => {
1✔
466
                let ptr_addr = (p.as_ptr() as usize, 0);
2✔
467
                self.cycles.get(&ptr_addr)
3✔
468
            }
469
            SteelVal::ListV(l) => {
20✔
470
                let ptr_addr = l.identity_tuple();
60✔
471

472
                self.cycles.get(&ptr_addr)
60✔
473
            }
474
            SteelVal::VectorV(l) => {
×
475
                let ptr_addr = (l.0.as_ptr() as usize, 0);
476

477
                self.cycles.get(&ptr_addr)
478
            }
479
            SteelVal::HashMapV(l) => {
×
480
                let ptr_addr = (l.0.as_ptr() as usize, 0);
×
481

482
                self.cycles.get(&ptr_addr)
×
483
            }
484
            SteelVal::HashSetV(l) => {
×
485
                let ptr_addr = (l.0.as_ptr() as usize, 0);
×
486

487
                self.cycles.get(&ptr_addr)
×
488
            }
489
            SteelVal::Custom(l) => {
×
490
                let ptr_addr = (l.as_ptr() as usize, 0);
×
491

492
                self.cycles.get(&ptr_addr)
×
493
            }
494
            SteelVal::Boxed(b) => {
×
495
                let ptr_addr = (b.as_ptr() as usize, 0);
×
496

497
                self.cycles.get(&ptr_addr)
×
498
            }
499
            SteelVal::SyntaxObject(s) => {
×
500
                let ptr_addr = (s.as_ptr() as usize, 0);
×
501

502
                self.cycles.get(&ptr_addr)
×
503
            }
504
            _ => None,
×
505
        }
506
        .copied()
507
    }
508

509
    pub fn values(&self) -> List<SteelVal> {
10,959✔
510
        self.values.clone()
21,918✔
511
    }
512
}
513

514
struct CycleCollector<'a> {
515
    // Keep a mapping of the pointer -> gensym
516
    visited: fxhash::FxHashSet<(usize, usize)>,
517

518
    // Recording things that have already been seen
519
    cycles: fxhash::FxHashMap<(usize, usize), usize>,
520

521
    // Values captured in cycles
522
    values: Vec<SteelVal>,
523

524
    // Queue of items to check
525
    queue: &'a mut Vec<SteelVal>,
526

527
    // Whether we found something mutable - if we haven't, then a cycle
528
    // isn't even possible
529
    found_mutable: bool,
530
}
531

532
impl<'a> CycleCollector<'a> {
533
    fn add(&mut self, val: (usize, usize), steelval: &SteelVal) -> bool {
441✔
534
        if !self.found_mutable {
441✔
535
            return false;
277✔
536
        }
537

538
        if self.visited.contains(&val) {
492✔
539
            let id = self.cycles.len();
84✔
540

541
            // If we've already seen this, its fine, we can just move on
542
            if let std::collections::hash_map::Entry::Vacant(e) = self.cycles.entry(val) {
84✔
543
                e.insert(id);
×
544
                // Keep track of the actual values that are being captured
545
                self.values.push(steelval.clone());
×
546
            } else {
547
                return true;
×
548
            }
549

550
            return true;
×
551
        }
552

553
        self.visited.insert(val);
×
554
        false
×
555
    }
556
}
557

558
impl<'a> BreadthFirstSearchSteelValVisitor for CycleCollector<'a> {
559
    type Output = ();
560

561
    fn default_output(&mut self) -> Self::Output {}
46,840✔
562

563
    fn pop_front(&mut self) -> Option<SteelVal> {
47,700✔
564
        self.queue.pop()
95,400✔
565
    }
566

567
    fn push_back(&mut self, value: SteelVal) {
24,280✔
568
        self.queue.push(value)
72,840✔
569
    }
570

571
    fn visit_bytevector(&mut self, _bytevector: SteelByteVector) -> Self::Output {}
×
572
    fn visit_closure(&mut self, _closure: Gc<ByteCodeLambda>) -> Self::Output {}
34✔
573
    fn visit_bool(&mut self, _boolean: bool) -> Self::Output {}
228✔
574
    fn visit_float(&mut self, _float: f64) -> Self::Output {}
178✔
575
    fn visit_int(&mut self, _int: isize) -> Self::Output {}
1,916✔
576
    fn visit_rational(&mut self, _: Rational32) -> Self::Output {}
18✔
577
    fn visit_bigrational(&mut self, _: Gc<BigRational>) -> Self::Output {}
4✔
578
    fn visit_bignum(&mut self, _: Gc<BigInt>) -> Self::Output {}
×
579
    fn visit_complex(&mut self, _: Gc<SteelComplex>) -> Self::Output {}
×
580
    fn visit_char(&mut self, _c: char) -> Self::Output {}
5,674✔
581

582
    fn visit_immutable_vector(&mut self, vector: SteelVector) -> Self::Output {
×
583
        if !self.add(
×
584
            (vector.0.as_ptr() as usize, 0),
×
585
            &SteelVal::VectorV(vector.clone()),
×
586
        ) {
587
            for value in vector.0.iter() {
×
588
                self.push_back(value.clone());
×
589
            }
590
        }
591
    }
592

593
    fn visit_void(&mut self) -> Self::Output {}
58✔
594
    fn visit_string(&mut self, _string: SteelString) -> Self::Output {}
38,928✔
595
    fn visit_function_pointer(&mut self, _ptr: FunctionSignature) -> Self::Output {}
16✔
596
    fn visit_symbol(&mut self, _symbol: SteelString) -> Self::Output {}
600✔
597

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

602
    fn visit_hash_map(&mut self, hashmap: SteelHashMap) -> Self::Output {
5✔
603
        if !self.add(
15✔
604
            (hashmap.0.as_ptr() as usize, 0),
10✔
605
            &SteelVal::HashMapV(hashmap.clone()),
5✔
606
        ) {
607
            for (key, value) in hashmap.0.iter() {
9✔
608
                self.push_back(key.clone());
×
609
                self.push_back(value.clone());
×
610
            }
611
        }
612
    }
613

614
    fn visit_hash_set(&mut self, hashset: SteelHashSet) -> Self::Output {
4✔
615
        if !self.add(
12✔
616
            (hashset.0.as_ptr() as usize, 0),
8✔
617
            &SteelVal::HashSetV(hashset.clone()),
4✔
618
        ) {
619
            for key in hashset.0.iter() {
6✔
620
                self.push_back(key.clone())
×
621
            }
622
        }
623
    }
624

625
    fn visit_steel_struct(&mut self, steel_struct: Gc<UserDefinedStruct>) -> Self::Output {
74✔
626
        if !self.add(
222✔
627
            (steel_struct.as_ptr() as usize, 0),
148✔
628
            &SteelVal::CustomStruct(steel_struct.clone()),
74✔
629
        ) {
630
            for value in steel_struct.fields.iter() {
167✔
631
                self.push_back(value.clone())
×
632
            }
633
        }
634
    }
635

636
    fn visit_port(&mut self, _port: SteelPort) -> Self::Output {}
×
637
    fn visit_transducer(&mut self, _transducer: Gc<Transducer>) -> Self::Output {}
×
638
    fn visit_reducer(&mut self, _reducer: Gc<Reducer>) -> Self::Output {}
×
639
    fn visit_future_function(&mut self, _function: BoxedAsyncFunctionSignature) -> Self::Output {}
×
640
    fn visit_future(&mut self, _future: Gc<FutureResult>) -> Self::Output {}
×
641
    fn visit_stream(&mut self, _stream: Gc<LazyStream>) -> Self::Output {}
24✔
642
    fn visit_boxed_function(&mut self, _function: Gc<BoxedDynFunction>) -> Self::Output {}
×
643
    fn visit_continuation(&mut self, _continuation: Continuation) -> Self::Output {}
×
644

645
    fn visit_list(&mut self, list: List<SteelVal>) -> Self::Output {
226✔
646
        if !self.add(list.identity_tuple(), &SteelVal::ListV(list.clone())) {
1,130✔
647
            for value in list {
1,366✔
648
                self.push_back(value);
×
649
            }
650
        }
651
    }
652

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

655
    // TODO: Figure out the mutable vector first
656
    fn visit_mutable_vector(&mut self, vector: HeapRef<Vec<SteelVal>>) -> Self::Output {
4✔
657
        self.found_mutable = true;
4✔
658

659
        if !self.add(
12✔
660
            (vector.as_ptr_usize(), 0),
8✔
661
            &SteelVal::MutableVector(vector.clone()),
4✔
662
        ) {
663
            for value in vector.get().iter() {
11✔
664
                self.push_back(value.clone());
×
665
            }
666
        }
667
    }
668

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

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

673
    fn visit_syntax_object(&mut self, syntax_object: Gc<Syntax>) -> Self::Output {
6✔
674
        if !self.add(
18✔
675
            (syntax_object.as_ptr() as usize, 0),
12✔
676
            &SteelVal::SyntaxObject(syntax_object.clone()),
6✔
677
        ) {
678
            if let Some(raw) = syntax_object.raw.clone() {
12✔
679
                self.push_back(raw);
×
680
            }
681

682
            self.push_back(syntax_object.syntax.clone());
×
683
        }
684
    }
685

686
    fn visit_boxed_value(&mut self, boxed_value: GcMut<SteelVal>) -> Self::Output {
×
687
        if !self.add(
×
688
            (boxed_value.as_ptr() as usize, 0),
×
689
            &SteelVal::Boxed(boxed_value.clone()),
×
690
        ) {
691
            self.push_back(boxed_value.read().clone());
×
692
        }
693
    }
694

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

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

700
        if !self.add(
312✔
701
            (heap_ref.as_ptr_usize(), 0),
208✔
702
            &SteelVal::HeapAllocated(heap_ref.clone()),
104✔
703
        ) {
704
            self.push_back(heap_ref.get());
104✔
705
        }
706
    }
707

708
    // TODO: Revisit this!
709
    fn visit_pair(&mut self, pair: Gc<Pair>) -> Self::Output {
18✔
710
        if !self.add((pair.as_ptr() as usize, 0), &SteelVal::Pair(pair.clone())) {
90✔
711
            self.push_back(pair.car());
18✔
712
            self.push_back(pair.cdr());
18✔
713
        }
714
    }
715
}
716

717
#[cfg(not(feature = "without-drop-protection"))]
718
pub(crate) mod drop_impls {
719
    // use crate::values::recycler::{Recyclable, Recycle};
720

721
    use super::*;
722

723
    thread_local! {
724
        pub static DROP_BUFFER: RefCell<VecDeque<SteelVal>> = RefCell::new(VecDeque::with_capacity(128));
725
        pub static FORMAT_BUFFER: RefCell<VecDeque<SteelVal>> = RefCell::new(VecDeque::with_capacity(128));
726
    }
727

728
    impl Drop for SteelVector {
729
        fn drop(&mut self) {
3,462,550✔
730
            if self.0.is_empty() {
3,462,550✔
731
                return;
6,776✔
732
            }
733

734
            if let Some(inner) = self.0.get_mut() {
2,449✔
735
                DROP_BUFFER
736
                    .try_with(|drop_buffer| {
2,449✔
737
                        if let Ok(mut drop_buffer) = drop_buffer.try_borrow_mut() {
4,898✔
738
                            for value in std::mem::take(inner) {
49,994✔
739
                                drop_buffer.push_back(value);
740
                            }
741

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

750
    impl Drop for SteelHashMap {
751
        fn drop(&mut self) {
252,051✔
752
            if self.0.is_empty() {
252,051✔
753
                return;
102,463✔
754
            }
755

756
            if let Some(inner) = self.0.get_mut() {
3,033✔
757
                DROP_BUFFER
758
                    .try_with(|drop_buffer| {
3,033✔
759
                        if let Ok(mut drop_buffer) = drop_buffer.try_borrow_mut() {
6,066✔
760
                            for (key, value) in std::mem::take(inner) {
31,834✔
761
                                drop_buffer.push_back(key);
762
                                drop_buffer.push_back(value);
763
                            }
764

765
                            IterativeDropHandler::bfs(&mut drop_buffer);
766
                        }
767
                    })
768
                    .ok();
769
            }
770
        }
771
    }
772

773
    impl Drop for UserDefinedStruct {
774
        fn drop(&mut self) {
108,499✔
775
            if self.fields.is_empty() {
108,499✔
776
                return;
4,905✔
777
            }
778

779
            if DROP_BUFFER
780
                .try_with(|drop_buffer| {
103,594✔
781
                    if let Ok(mut drop_buffer) = drop_buffer.try_borrow_mut() {
207,123✔
782
                        // for value in std::mem::take(&mut self.fields) {
783
                        //     drop_buffer.push_back(value);
784
                        // }
785

786
                        drop_buffer.extend(
787
                            self.fields.drain(..),
788
                            // std::mem::replace(&mut self.fields, Recycle::noop()).into_iter(),
789
                        );
790

791
                        // std::mem::replace(&mut self, self.fields.put();
792

793
                        IterativeDropHandler::bfs(&mut drop_buffer);
794
                    }
795
                })
796
                .is_err()
797
            {
798
                let mut buffer = self.fields.drain(..).collect();
×
799

800
                IterativeDropHandler::bfs(&mut buffer);
×
801
            }
802
        }
803
    }
804

805
    impl Drop for LazyStream {
806
        fn drop(&mut self) {
116✔
807
            if self.initial_value == SteelVal::Void && self.stream_thunk == SteelVal::Void {
118✔
808
                return;
2✔
809
            }
810

811
            DROP_BUFFER
812
                .try_with(|drop_buffer| {
114✔
813
                    if let Ok(mut drop_buffer) = drop_buffer.try_borrow_mut() {
228✔
814
                        drop_buffer.push_back(self.initial_value.make_void());
815
                        drop_buffer.push_back(self.stream_thunk.make_void());
816

817
                        IterativeDropHandler::bfs(&mut drop_buffer);
818
                    }
819
                })
820
                .ok();
821
        }
822
    }
823

824
    // impl Drop for ByteCodeLambda {
825
    //     fn drop(&mut self) {
826
    //         if self.captures.is_empty() {
827
    //             return;
828
    //         }
829

830
    //         DROP_BUFFER
831
    //             .try_with(|drop_buffer| {
832
    //                 if let Ok(mut drop_buffer) = drop_buffer.try_borrow_mut() {
833
    //                     for value in std::mem::take(&mut self.captures) {
834
    //                         drop_buffer.push_back(value);
835
    //                     }
836

837
    //                     IterativeDropHandler::bfs(&mut drop_buffer);
838
    //                 }
839
    //             })
840
    //             .ok();
841
    //     }
842
    // }
843
}
844

845
pub struct IterativeDropHandler<'a> {
846
    drop_buffer: &'a mut VecDeque<SteelVal>,
847
    #[cfg(feature = "experimental-drop-handler")]
848
    moved_threads: bool,
849
}
850

851
impl<'a> IterativeDropHandler<'a> {
852
    pub fn bfs(drop_buffer: &'a mut VecDeque<SteelVal>) {
5,467,637✔
853
        IterativeDropHandler {
854
            drop_buffer,
855
            #[cfg(feature = "experimental-drop-handler")]
856
            moved_threads: false,
857
        }
858
        .visit();
859
    }
860
}
861

862
impl<'a> BreadthFirstSearchSteelValVisitor for IterativeDropHandler<'a> {
863
    type Output = ();
864

865
    fn default_output(&mut self) -> Self::Output {}
10,935,274✔
866

867
    fn pop_front(&mut self) -> Option<SteelVal> {
9,436,393✔
868
        self.drop_buffer.pop_front()
18,872,786✔
869
    }
870

871
    fn push_back(&mut self, value: SteelVal) {
8,623,969✔
872
        match &value {
8,623,969✔
873
            SteelVal::BoolV(_)
×
874
            | SteelVal::NumV(_)
×
875
            | SteelVal::IntV(_)
×
876
            | SteelVal::CharV(_)
×
877
            | SteelVal::Void
×
878
            | SteelVal::StringV(_)
×
879
            | SteelVal::FuncV(_)
×
880
            | SteelVal::SymbolV(_)
×
881
            | SteelVal::FutureFunc(_)
×
882
            | SteelVal::FutureV(_)
×
883
            | SteelVal::BoxedFunction(_)
×
884
            | SteelVal::MutFunc(_)
×
885
            | SteelVal::BuiltIn(_)
×
886
            | SteelVal::ByteVector(_)
×
887
            | SteelVal::BigNum(_) => (),
5,415,543✔
888
            _ => {
3,208,426✔
889
                self.drop_buffer.push_back(value);
6,416,852✔
890
            }
891
        }
892
    }
893

894
    fn visit_bytevector(&mut self, _bytevector: SteelByteVector) -> Self::Output {}
30✔
895
    fn visit_bool(&mut self, _boolean: bool) {}
7,442✔
896
    fn visit_float(&mut self, _float: f64) {}
80,656✔
897
    fn visit_int(&mut self, _int: isize) {}
1,736✔
898
    fn visit_rational(&mut self, _: Rational32) {}
6✔
899
    fn visit_bigrational(&mut self, _: Gc<BigRational>) {}
×
900
    fn visit_char(&mut self, _c: char) {}
84✔
901
    fn visit_void(&mut self) {}
5,320✔
902
    fn visit_string(&mut self, _string: SteelString) {}
264✔
903
    fn visit_function_pointer(&mut self, _ptr: FunctionSignature) {}
1,128✔
904
    fn visit_symbol(&mut self, _symbol: SteelString) {}
64,822✔
905
    fn visit_port(&mut self, _port: SteelPort) {}
363,646✔
906
    fn visit_future(&mut self, _future: Gc<FutureResult>) {}
×
907
    fn visit_mutable_function(&mut self, _function: MutFunctionSignature) {}
×
908
    fn visit_complex(&mut self, _: Gc<SteelComplex>) {}
2✔
909
    fn visit_bignum(&mut self, _bignum: Gc<BigInt>) {}
×
910
    fn visit_future_function(&mut self, _function: BoxedAsyncFunctionSignature) {}
×
911
    fn visit_builtin_function(&mut self, _function: BuiltInSignature) {}
×
912
    fn visit_boxed_function(&mut self, _function: Gc<BoxedDynFunction>) {}
4,904✔
913

914
    fn visit_closure(&mut self, closure: Gc<ByteCodeLambda>) {
35,615✔
915
        if let Ok(mut inner) = closure.try_unwrap() {
42,810✔
916
            for value in std::mem::take(&mut inner.captures) {
8,586✔
917
                self.push_back(value);
×
918
            }
919
        }
920
    }
921

922
    fn visit_immutable_vector(&mut self, mut vector: SteelVector) {
47,130✔
923
        if let Some(inner) = vector.0.get_mut() {
53,843✔
924
            for value in std::mem::take(inner) {
26,735✔
925
                self.push_back(value);
×
926
            }
927
        }
928
    }
929

930
    fn visit_custom_type(&mut self, custom_type: GcMut<Box<dyn CustomType>>) {
2,318✔
931
        if let Ok(inner) = custom_type.try_unwrap() {
2,318✔
932
            let mut inner = inner.consume();
×
933

934
            // let this decide if we're doing anything with this custom type
935
            inner.drop_mut(self);
×
936
        }
937
    }
938

939
    fn visit_hash_map(&mut self, mut hashmap: SteelHashMap) {
100,014✔
940
        if let Some(inner) = hashmap.0.get_mut() {
200,014✔
941
            for (key, value) in std::mem::take(inner) {
100,000✔
942
                self.push_back(key);
×
943
                self.push_back(value);
×
944
            }
945
        }
946
    }
947

948
    fn visit_hash_set(&mut self, mut hashset: SteelHashSet) {
4✔
949
        if let Some(inner) = hashset.0.get_mut() {
4✔
950
            for key in std::mem::take(inner) {
×
951
                self.push_back(key);
×
952
            }
953
        }
954
    }
955

956
    fn visit_steel_struct(&mut self, steel_struct: Gc<UserDefinedStruct>) {
11,180✔
957
        if let Ok(mut inner) = steel_struct.try_unwrap() {
16,084✔
958
            for value in inner.fields.drain(..) {
12,822✔
959
                self.push_back(value);
×
960
            }
961
        }
962
    }
963

964
    fn visit_transducer(&mut self, transducer: Gc<Transducer>) {
×
965
        if let Ok(inner) = transducer.try_unwrap() {
×
966
            for transducer in inner.ops {
×
967
                match transducer {
×
968
                    crate::values::transducers::Transducers::Map(m) => self.push_back(m),
×
969
                    crate::values::transducers::Transducers::Filter(v) => self.push_back(v),
×
970
                    crate::values::transducers::Transducers::Take(t) => self.push_back(t),
×
971
                    crate::values::transducers::Transducers::Drop(d) => self.push_back(d),
×
972
                    crate::values::transducers::Transducers::FlatMap(fm) => self.push_back(fm),
×
973
                    crate::values::transducers::Transducers::Flatten => {}
×
974
                    crate::values::transducers::Transducers::Window(w) => self.push_back(w),
×
975
                    crate::values::transducers::Transducers::TakeWhile(tw) => self.push_back(tw),
×
976
                    crate::values::transducers::Transducers::DropWhile(dw) => self.push_back(dw),
×
977
                    crate::values::transducers::Transducers::Extend(e) => self.push_back(e),
×
978
                    crate::values::transducers::Transducers::Cycle => {}
×
979
                    crate::values::transducers::Transducers::Enumerating => {}
×
980
                    crate::values::transducers::Transducers::Zipping(z) => self.push_back(z),
×
981
                    crate::values::transducers::Transducers::Interleaving(i) => self.push_back(i),
×
982
                }
983
            }
984
        }
985
    }
986

987
    fn visit_reducer(&mut self, reducer: Gc<Reducer>) {
×
988
        if let Ok(inner) = reducer.try_unwrap() {
×
989
            match inner {
×
990
                Reducer::ForEach(f) => self.push_back(f),
×
991
                Reducer::Generic(rf) => {
×
992
                    self.push_back(rf.initial_value);
×
993
                    self.push_back(rf.function);
×
994
                }
995
                _ => {}
×
996
            }
997
        }
998
    }
999

1000
    fn visit_stream(&mut self, stream: Gc<LazyStream>) {
14✔
1001
        if let Ok(mut inner) = stream.try_unwrap() {
16✔
1002
            self.push_back(replace_with_void(&mut inner.initial_value));
×
1003
            self.push_back(replace_with_void(&mut inner.stream_thunk));
×
1004
        }
1005
    }
1006

1007
    // Walk the whole thing! This includes the stack and all the stack frames
1008
    fn visit_continuation(&mut self, continuation: Continuation) {
797✔
1009
        if let Ok(inner) =
56✔
1010
            crate::gc::shared::StandardShared::try_unwrap(continuation.inner).map(|x| x.consume())
2,503✔
1011
        {
1012
            match inner {
×
1013
                ContinuationMark::Closed(mut inner) => {
24✔
1014
                    for value in std::mem::take(&mut inner.stack) {
176✔
1015
                        self.push_back(value);
×
1016
                    }
1017

1018
                    if let Some(inner) = inner.current_frame.function.get_mut() {
×
1019
                        for value in std::mem::take(&mut inner.captures) {
×
1020
                            self.push_back(value);
×
1021
                        }
1022
                    }
1023

1024
                    for mut frame in std::mem::take(&mut inner.stack_frames) {
99✔
1025
                        if let Some(inner) = frame.function.get_mut() {
8✔
1026
                            for value in std::mem::take(&mut inner.captures) {
10✔
1027
                                self.push_back(value);
×
1028
                            }
1029
                        }
1030
                    }
1031
                }
1032

1033
                ContinuationMark::Open(mut inner) => {
32✔
1034
                    for value in inner.current_stack_values {
96✔
1035
                        self.push_back(value);
×
1036
                    }
1037

1038
                    if let Some(inner) = inner.current_frame.function.get_mut() {
32✔
1039
                        for value in std::mem::take(&mut inner.captures) {
×
1040
                            self.push_back(value);
×
1041
                        }
1042
                    }
1043
                }
1044
            }
1045
        }
1046
    }
1047

1048
    fn visit_list(&mut self, list: List<SteelVal>) {
3,146,500✔
1049
        // println!("VISITING LIST: {}", list.strong_count());
1050
        // println!("list: {:?}", list);
1051

1052
        if list.strong_count() == 1 {
3,146,500✔
1053
            for value in list.draining_iterator() {
14,478,261✔
1054
                // println!(
1055
                // "PUSHING BACK VALUE - queue size: {}",
1056
                // self.drop_buffer.len()
1057
                // );
1058

1059
                // println!("enqueueing: {}", value);
1060

1061
                self.push_back(value);
×
1062
            }
1063
        }
1064
    }
1065

1066
    // TODO: When this gets replaced with heap storage, then we can do this more
1067
    // effectively!
1068
    fn visit_mutable_vector(&mut self, _vector: HeapRef<Vec<SteelVal>>) {}
168,924✔
1069

1070
    // TODO: Once the root is added back to this, bring it back
1071
    fn visit_boxed_iterator(&mut self, iterator: GcMut<OpaqueIterator>) {
×
1072
        if let Ok(inner) = iterator.try_unwrap() {
×
1073
            self.push_back(inner.consume().root)
×
1074
        }
1075
    }
1076

1077
    fn visit_syntax_object(&mut self, syntax_object: Gc<Syntax>) {
14,811✔
1078
        if let Ok(inner) = syntax_object.try_unwrap() {
29,544✔
1079
            if let Some(raw) = inner.raw {
14,712✔
1080
                self.push_back(raw);
×
1081
            }
1082

1083
            self.push_back(inner.syntax);
×
1084
        }
1085
    }
1086

1087
    fn visit_boxed_value(&mut self, boxed_value: GcMut<SteelVal>) {
×
1088
        if let Ok(inner) = boxed_value.try_unwrap() {
×
1089
            self.push_back(inner.consume());
×
1090
        }
1091
    }
1092

1093
    fn visit_reference_value(&mut self, reference: Gc<OpaqueReference<'static>>) {
×
1094
        if let Ok(mut inner) = Gc::try_unwrap(reference) {
×
1095
            inner.drop_mut(self);
×
1096
        }
1097
    }
1098

1099
    fn visit_heap_allocated(&mut self, _heap_ref: HeapRef<SteelVal>) -> Self::Output {}
406,504✔
1100

1101
    // TODO: After a certain point, we should just pause
1102
    // and continue the iteration on another thread. That will
1103
    // help with long drops for recursive data structures.
1104
    fn visit(&mut self) -> Self::Output {
5,467,637✔
1105
        let mut ret = self.default_output();
16,402,911✔
1106

1107
        while let Some(value) = self.pop_front() {
13,405,149✔
1108
            ret = match value {
×
1109
                Closure(c) => self.visit_closure(c),
35,615✔
1110
                BoolV(b) => self.visit_bool(b),
14,884✔
1111
                NumV(n) => self.visit_float(n),
161,312✔
1112
                IntV(i) => self.visit_int(i),
3,472✔
1113
                Rational(x) => self.visit_rational(x),
12✔
1114
                BigRational(x) => self.visit_bigrational(x),
×
1115
                BigNum(b) => self.visit_bignum(b),
×
1116
                Complex(x) => self.visit_complex(x),
4✔
1117
                CharV(c) => self.visit_char(c),
168✔
1118
                VectorV(v) => self.visit_immutable_vector(v),
188,520✔
1119
                Void => self.visit_void(),
5,320✔
1120
                StringV(s) => self.visit_string(s),
528✔
1121
                FuncV(f) => self.visit_function_pointer(f),
2,256✔
1122
                SymbolV(s) => self.visit_symbol(s),
129,644✔
1123
                SteelVal::Custom(c) => self.visit_custom_type(c),
9,272✔
1124
                HashMapV(h) => self.visit_hash_map(h),
400,056✔
1125
                HashSetV(s) => self.visit_hash_set(s),
16✔
1126
                CustomStruct(c) => self.visit_steel_struct(c),
44,720✔
1127
                PortV(p) => self.visit_port(p),
727,292✔
1128
                IterV(t) => self.visit_transducer(t),
×
1129
                ReducerV(r) => self.visit_reducer(r),
×
1130
                FutureFunc(f) => self.visit_future_function(f),
×
1131
                FutureV(f) => self.visit_future(f),
×
1132
                StreamV(s) => self.visit_stream(s),
56✔
1133
                BoxedFunction(b) => self.visit_boxed_function(b),
9,808✔
1134
                ContinuationFunction(c) => self.visit_continuation(c),
3,188✔
1135
                ListV(l) => self.visit_list(l),
12,586,000✔
1136
                MutFunc(m) => self.visit_mutable_function(m),
×
1137
                BuiltIn(b) => self.visit_builtin_function(b),
×
1138
                MutableVector(b) => self.visit_mutable_vector(b),
337,848✔
1139
                BoxedIterator(b) => self.visit_boxed_iterator(b),
×
1140
                SteelVal::SyntaxObject(s) => self.visit_syntax_object(s),
59,244✔
1141
                Boxed(b) => self.visit_boxed_value(b),
×
1142
                Reference(r) => self.visit_reference_value(r),
×
1143
                HeapAllocated(b) => self.visit_heap_allocated(b),
813,008✔
1144
                Pair(p) => self.visit_pair(p),
230,556✔
1145
                ByteVector(b) => self.visit_bytevector(b),
60✔
1146
            };
1147

1148
            // Long recursive drops will block the main thread from continuing - we should
1149
            // have another thread pick up the work?
1150
            #[cfg(feature = "experimental-drop-handler")]
×
1151
            if !self.moved_threads && self.drop_buffer.len() > 20 {
×
1152
                self.moved_threads = true;
×
1153

1154
                static DROP_THREAD: once_cell::sync::Lazy<
×
1155
                    crossbeam_channel::Sender<OwnedIterativeDropHandler>,
×
1156
                > = once_cell::sync::Lazy::new(start_background_drop_thread);
×
1157

1158
                fn start_background_drop_thread(
×
1159
                ) -> crossbeam_channel::Sender<OwnedIterativeDropHandler> {
1160
                    let (sender, receiver) =
×
1161
                        crossbeam_channel::unbounded::<OwnedIterativeDropHandler>();
×
1162

1163
                    std::thread::spawn(move || {
×
1164
                        while let Ok(mut value) = receiver.recv() {
×
1165
                            // let now = std::time::Instant::now();
1166
                            value.visit();
×
1167
                            // println!("Dropping: {:?}", now.elapsed());
1168
                        }
1169
                    });
1170

1171
                    sender
×
1172
                }
1173

1174
                // let buffer = VecDeque::new();
1175
                let original_buffer = std::mem::replace(self.drop_buffer, VecDeque::new());
×
1176

1177
                // println!("Moving to another thread");
1178

1179
                DROP_THREAD
×
1180
                    .send(OwnedIterativeDropHandler {
×
1181
                        drop_buffer: original_buffer,
×
1182
                    })
1183
                    .ok();
1184

1185
                // std::thread::spawn(move || {
1186
                //     let mut handler = OwnedIterativeDropHandler {
1187
                //         drop_buffer: original_buffer,
1188
                //     };
1189

1190
                //     handler.visit();
1191

1192
                //     drop(handler)
1193
                // });
1194
            }
1195
        }
1196

1197
        ret
5,467,637✔
1198
    }
1199

1200
    fn visit_pair(&mut self, pair: Gc<Pair>) -> Self::Output {
57,639✔
1201
        if let Ok(inner) = Gc::try_unwrap(pair) {
92,381✔
1202
            self.push_back(inner.car);
×
1203
            self.push_back(inner.cdr);
×
1204
        }
1205
    }
1206
}
1207

1208
// TODO: Figure out a more elegant way to do this!
1209

1210
#[cfg(feature = "experimental-drop-handler")]
1211
pub struct OwnedIterativeDropHandler {
1212
    drop_buffer: VecDeque<SteelVal>,
1213
}
1214

1215
#[cfg(feature = "experimental-drop-handler")]
1216
impl BreadthFirstSearchSteelValVisitor for OwnedIterativeDropHandler {
1217
    type Output = ();
1218

1219
    fn default_output(&mut self) -> Self::Output {
1220
        ()
1221
    }
1222

1223
    fn pop_front(&mut self) -> Option<SteelVal> {
1224
        self.drop_buffer.pop_front()
1225
    }
1226

1227
    fn push_back(&mut self, value: SteelVal) {
1228
        match &value {
1229
            SteelVal::BoolV(_)
1230
            | SteelVal::NumV(_)
1231
            | SteelVal::IntV(_)
1232
            | SteelVal::CharV(_)
1233
            | SteelVal::Void
1234
            | SteelVal::StringV(_)
1235
            | SteelVal::FuncV(_)
1236
            | SteelVal::SymbolV(_)
1237
            | SteelVal::FutureFunc(_)
1238
            | SteelVal::FutureV(_)
1239
            | SteelVal::BoxedFunction(_)
1240
            | SteelVal::MutFunc(_)
1241
            | SteelVal::BuiltIn(_)
1242
            | SteelVal::ByteVector(_)
1243
            | SteelVal::BigNum(_) => return,
1244
            _ => {
1245
                self.drop_buffer.push_back(value);
1246
            }
1247
        }
1248
    }
1249

1250
    fn visit_bytevector(&mut self, _bytevector: SteelByteVector) -> Self::Output {}
1251
    fn visit_bool(&mut self, _boolean: bool) {}
1252
    fn visit_float(&mut self, _float: f64) {}
1253
    fn visit_int(&mut self, _int: isize) {}
1254
    fn visit_rational(&mut self, _: Rational32) {}
1255
    fn visit_bigrational(&mut self, _: Gc<BigRational>) {}
1256
    fn visit_char(&mut self, _c: char) {}
1257
    fn visit_void(&mut self) {}
1258
    fn visit_string(&mut self, _string: SteelString) {}
1259
    fn visit_function_pointer(&mut self, _ptr: FunctionSignature) {}
1260
    fn visit_symbol(&mut self, _symbol: SteelString) {}
1261
    fn visit_port(&mut self, _port: SteelPort) {}
1262
    fn visit_future(&mut self, _future: Gc<FutureResult>) {}
1263
    fn visit_mutable_function(&mut self, _function: MutFunctionSignature) {}
1264
    fn visit_complex(&mut self, _: Gc<SteelComplex>) {}
1265
    fn visit_bignum(&mut self, _bignum: Gc<BigInt>) {}
1266
    fn visit_future_function(&mut self, _function: BoxedAsyncFunctionSignature) {}
1267
    fn visit_builtin_function(&mut self, _function: BuiltInSignature) {}
1268
    fn visit_boxed_function(&mut self, _function: Gc<BoxedDynFunction>) {}
1269

1270
    fn visit_closure(&mut self, closure: Gc<ByteCodeLambda>) {
1271
        if let Ok(mut inner) = closure.try_unwrap() {
1272
            for value in std::mem::take(&mut inner.captures) {
1273
                self.push_back(value);
1274
            }
1275
        }
1276
    }
1277

1278
    fn visit_immutable_vector(&mut self, mut vector: SteelVector) {
1279
        if let Some(inner) = vector.0.get_mut() {
1280
            for value in std::mem::take(inner) {
1281
                self.push_back(value);
1282
            }
1283
        }
1284
    }
1285

1286
    fn visit_custom_type(&mut self, custom_type: GcMut<Box<dyn CustomType>>) {
1287
        if let Ok(inner) = custom_type.try_unwrap() {
1288
            let mut inner = inner.consume();
1289

1290
            // TODO: @matt - Don't leave this while merging
1291
            // inner.drop_mut(self);
1292

1293
            // let this decide if we're doing anything with this custom type
1294
            // inner.drop_mut(self);
1295
        }
1296
    }
1297

1298
    fn visit_hash_map(&mut self, mut hashmap: SteelHashMap) {
1299
        if let Some(inner) = hashmap.0.get_mut() {
1300
            for (key, value) in std::mem::take(inner) {
1301
                self.push_back(key);
1302
                self.push_back(value);
1303
            }
1304
        }
1305
    }
1306

1307
    fn visit_hash_set(&mut self, mut hashset: SteelHashSet) {
1308
        if let Some(inner) = hashset.0.get_mut() {
1309
            for key in std::mem::take(inner) {
1310
                self.push_back(key);
1311
            }
1312
        }
1313
    }
1314

1315
    fn visit_steel_struct(&mut self, steel_struct: Gc<UserDefinedStruct>) {
1316
        if let Ok(mut inner) = steel_struct.try_unwrap() {
1317
            for value in inner.fields.drain(..) {
1318
                self.push_back(value);
1319
            }
1320
        }
1321
    }
1322

1323
    fn visit_transducer(&mut self, transducer: Gc<Transducer>) {
1324
        if let Ok(inner) = transducer.try_unwrap() {
1325
            for transducer in inner.ops {
1326
                match transducer {
1327
                    crate::values::transducers::Transducers::Map(m) => self.push_back(m),
1328
                    crate::values::transducers::Transducers::Filter(v) => self.push_back(v),
1329
                    crate::values::transducers::Transducers::Take(t) => self.push_back(t),
1330
                    crate::values::transducers::Transducers::Drop(d) => self.push_back(d),
1331
                    crate::values::transducers::Transducers::FlatMap(fm) => self.push_back(fm),
1332
                    crate::values::transducers::Transducers::Flatten => {}
1333
                    crate::values::transducers::Transducers::Window(w) => self.push_back(w),
1334
                    crate::values::transducers::Transducers::TakeWhile(tw) => self.push_back(tw),
1335
                    crate::values::transducers::Transducers::DropWhile(dw) => self.push_back(dw),
1336
                    crate::values::transducers::Transducers::Extend(e) => self.push_back(e),
1337
                    crate::values::transducers::Transducers::Cycle => {}
1338
                    crate::values::transducers::Transducers::Enumerating => {}
1339
                    crate::values::transducers::Transducers::Zipping(z) => self.push_back(z),
1340
                    crate::values::transducers::Transducers::Interleaving(i) => self.push_back(i),
1341
                }
1342
            }
1343
        }
1344
    }
1345

1346
    fn visit_reducer(&mut self, reducer: Gc<Reducer>) {
1347
        if let Ok(inner) = reducer.try_unwrap() {
1348
            match inner {
1349
                Reducer::ForEach(f) => self.push_back(f),
1350
                Reducer::Generic(rf) => {
1351
                    self.push_back(rf.initial_value);
1352
                    self.push_back(rf.function);
1353
                }
1354
                _ => {}
1355
            }
1356
        }
1357
    }
1358

1359
    fn visit_stream(&mut self, stream: Gc<LazyStream>) {
1360
        if let Ok(mut inner) = stream.try_unwrap() {
1361
            self.push_back(replace_with_void(&mut inner.initial_value));
1362
            self.push_back(replace_with_void(&mut inner.stream_thunk));
1363
        }
1364
    }
1365

1366
    // Walk the whole thing! This includes the stack and all the stack frames
1367
    fn visit_continuation(&mut self, continuation: Continuation) {
1368
        if let Ok(inner) = crate::gc::Shared::try_unwrap(continuation.inner).map(|x| x.consume()) {
1369
            match inner {
1370
                ContinuationMark::Closed(mut inner) => {
1371
                    for value in std::mem::take(&mut inner.stack) {
1372
                        self.push_back(value);
1373
                    }
1374

1375
                    if let Some(inner) = inner.current_frame.function.get_mut() {
1376
                        for value in std::mem::take(&mut inner.captures) {
1377
                            self.push_back(value);
1378
                        }
1379
                    }
1380

1381
                    for mut frame in std::mem::take(&mut inner.stack_frames) {
1382
                        if let Some(inner) = frame.function.get_mut() {
1383
                            for value in std::mem::take(&mut inner.captures) {
1384
                                self.push_back(value);
1385
                            }
1386
                        }
1387
                    }
1388
                }
1389

1390
                ContinuationMark::Open(mut inner) => {
1391
                    for value in inner.current_stack_values {
1392
                        self.push_back(value);
1393
                    }
1394

1395
                    if let Some(inner) = inner.current_frame.function.get_mut() {
1396
                        for value in std::mem::take(&mut inner.captures) {
1397
                            self.push_back(value);
1398
                        }
1399
                    }
1400
                }
1401
            }
1402
        }
1403
    }
1404

1405
    fn visit_list(&mut self, list: List<SteelVal>) {
1406
        // println!("VISITING LIST: {}", list.strong_count());
1407
        // println!("list: {:?}", list);
1408

1409
        if list.strong_count() == 1 {
1410
            for value in list.draining_iterator() {
1411
                // println!(
1412
                // "PUSHING BACK VALUE - queue size: {}",
1413
                // self.drop_buffer.len()
1414
                // );
1415

1416
                // println!("enqueueing: {}", value);
1417

1418
                self.push_back(value);
1419
            }
1420
        }
1421
    }
1422

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

1427
    // TODO: Once the root is added back to this, bring it back
1428
    fn visit_boxed_iterator(&mut self, iterator: GcMut<OpaqueIterator>) {
1429
        if let Ok(inner) = iterator.try_unwrap() {
1430
            self.push_back(inner.consume().root)
1431
        }
1432
    }
1433

1434
    fn visit_syntax_object(&mut self, syntax_object: Gc<Syntax>) {
1435
        if let Ok(inner) = syntax_object.try_unwrap() {
1436
            if let Some(raw) = inner.raw {
1437
                self.push_back(raw);
1438
            }
1439

1440
            self.push_back(inner.syntax);
1441
        }
1442
    }
1443

1444
    fn visit_boxed_value(&mut self, boxed_value: GcMut<SteelVal>) {
1445
        if let Ok(inner) = boxed_value.try_unwrap() {
1446
            self.push_back(inner.consume());
1447
        }
1448
    }
1449

1450
    fn visit_reference_value(&mut self, reference: Gc<OpaqueReference<'static>>) {
1451
        if let Ok(mut inner) = Gc::try_unwrap(reference) {
1452
            // TODO: @matt - Don't leave this while merging
1453
            // inner.drop_mut(self);
1454
        }
1455
    }
1456

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

1459
    // TODO: After a certain point, we should just pause
1460
    // and continue the iteration on another thread. That will
1461
    // help with long drops for recursive data structures.
1462
    fn visit(&mut self) -> Self::Output {
1463
        let mut ret = self.default_output();
1464

1465
        while let Some(value) = self.pop_front() {
1466
            ret = match value {
1467
                Closure(c) => self.visit_closure(c),
1468
                BoolV(b) => self.visit_bool(b),
1469
                NumV(n) => self.visit_float(n),
1470
                IntV(i) => self.visit_int(i),
1471
                Rational(x) => self.visit_rational(x),
1472
                BigRational(x) => self.visit_bigrational(x),
1473
                BigNum(b) => self.visit_bignum(b),
1474
                Complex(x) => self.visit_complex(x),
1475
                CharV(c) => self.visit_char(c),
1476
                VectorV(v) => self.visit_immutable_vector(v),
1477
                Void => self.visit_void(),
1478
                StringV(s) => self.visit_string(s),
1479
                FuncV(f) => self.visit_function_pointer(f),
1480
                SymbolV(s) => self.visit_symbol(s),
1481
                SteelVal::Custom(c) => self.visit_custom_type(c),
1482
                HashMapV(h) => self.visit_hash_map(h),
1483
                HashSetV(s) => self.visit_hash_set(s),
1484
                CustomStruct(c) => self.visit_steel_struct(c),
1485
                PortV(p) => self.visit_port(p),
1486
                IterV(t) => self.visit_transducer(t),
1487
                ReducerV(r) => self.visit_reducer(r),
1488
                FutureFunc(f) => self.visit_future_function(f),
1489
                FutureV(f) => self.visit_future(f),
1490
                StreamV(s) => self.visit_stream(s),
1491
                BoxedFunction(b) => self.visit_boxed_function(b),
1492
                ContinuationFunction(c) => self.visit_continuation(c),
1493
                ListV(l) => self.visit_list(l),
1494
                MutFunc(m) => self.visit_mutable_function(m),
1495
                BuiltIn(b) => self.visit_builtin_function(b),
1496
                MutableVector(b) => self.visit_mutable_vector(b),
1497
                BoxedIterator(b) => self.visit_boxed_iterator(b),
1498
                SteelVal::SyntaxObject(s) => self.visit_syntax_object(s),
1499
                Boxed(b) => self.visit_boxed_value(b),
1500
                Reference(r) => self.visit_reference_value(r),
1501
                HeapAllocated(b) => self.visit_heap_allocated(b),
1502
                Pair(p) => self.visit_pair(p),
1503
                ByteVector(b) => self.visit_bytevector(b),
1504
            };
1505
        }
1506

1507
        ret
1508
    }
1509

1510
    fn visit_pair(&mut self, pair: Gc<Pair>) -> Self::Output {
1511
        if let Ok(inner) = Gc::try_unwrap(pair) {
1512
            self.push_back(inner.car);
1513
            self.push_back(inner.cdr);
1514
        }
1515
    }
1516
}
1517

1518
pub trait BreadthFirstSearchSteelValVisitor {
1519
    type Output;
1520

1521
    fn default_output(&mut self) -> Self::Output;
1522

1523
    fn pop_front(&mut self) -> Option<SteelVal>;
1524

1525
    fn push_back(&mut self, value: SteelVal);
1526

1527
    fn visit(&mut self) -> Self::Output {
23,420✔
1528
        let mut ret = self.default_output();
70,260✔
1529

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

1572
        ret
23,420✔
1573
    }
1574

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

1614
pub trait BreadthFirstSearchSteelValVisitor2 {
1615
    type Output;
1616

1617
    fn default_output(&mut self) -> Self::Output;
1618

1619
    fn pop_front(&mut self) -> Option<SteelVal>;
1620

1621
    fn push_back(&mut self, value: &SteelVal);
1622

1623
    fn visit(&mut self) -> Self::Output {
×
1624
        let mut ret = self.default_output();
×
1625

1626
        while let Some(value) = self.pop_front() {
×
1627
            ret = match value {
×
1628
                Closure(c) => self.visit_closure(c),
×
1629
                BoolV(b) => self.visit_bool(b),
×
1630
                NumV(n) => self.visit_float(n),
×
1631
                IntV(i) => self.visit_int(i),
×
1632
                Rational(x) => self.visit_rational(x),
×
1633
                BigRational(x) => self.visit_bigrational(x),
×
1634
                BigNum(b) => self.visit_bignum(b),
×
1635
                Complex(x) => self.visit_complex(x),
×
1636
                CharV(c) => self.visit_char(c),
×
1637
                VectorV(v) => self.visit_immutable_vector(v),
×
1638
                Void => self.visit_void(),
×
1639
                StringV(s) => self.visit_string(s),
×
1640
                FuncV(f) => self.visit_function_pointer(f),
×
1641
                SymbolV(s) => self.visit_symbol(s),
×
1642
                SteelVal::Custom(c) => self.visit_custom_type(c),
×
1643
                HashMapV(h) => self.visit_hash_map(h),
×
1644
                HashSetV(s) => self.visit_hash_set(s),
×
1645
                CustomStruct(c) => self.visit_steel_struct(c),
×
1646
                PortV(p) => self.visit_port(p),
×
1647
                IterV(t) => self.visit_transducer(t),
×
1648
                ReducerV(r) => self.visit_reducer(r),
×
1649
                FutureFunc(f) => self.visit_future_function(f),
×
1650
                FutureV(f) => self.visit_future(f),
×
1651
                StreamV(s) => self.visit_stream(s),
×
1652
                BoxedFunction(b) => self.visit_boxed_function(b),
×
1653
                ContinuationFunction(c) => self.visit_continuation(c),
×
1654
                ListV(l) => self.visit_list(l),
×
1655
                MutFunc(m) => self.visit_mutable_function(m),
×
1656
                BuiltIn(b) => self.visit_builtin_function(b),
×
1657
                MutableVector(b) => self.visit_mutable_vector(b),
×
1658
                BoxedIterator(b) => self.visit_boxed_iterator(b),
×
1659
                SteelVal::SyntaxObject(s) => self.visit_syntax_object(s),
×
1660
                Boxed(b) => self.visit_boxed_value(b),
×
1661
                Reference(r) => self.visit_reference_value(r),
×
1662
                HeapAllocated(b) => self.visit_heap_allocated(b),
×
1663
                Pair(p) => self.visit_pair(p),
×
1664
                ByteVector(b) => self.visit_bytevector(b),
×
1665
            };
1666
        }
1667

1668
        ret
×
1669
    }
1670

1671
    fn visit_closure(&mut self, closure: Gc<ByteCodeLambda>) -> Self::Output;
1672
    fn visit_bool(&mut self, _: bool) -> Self::Output;
1673
    fn visit_float(&mut self, _: f64) -> Self::Output;
1674
    fn visit_int(&mut self, _: isize) -> Self::Output;
1675
    fn visit_rational(&mut self, _: Rational32) -> Self::Output;
1676
    fn visit_bigrational(&mut self, _: Gc<BigRational>) -> Self::Output;
1677
    fn visit_bignum(&mut self, _: Gc<BigInt>) -> Self::Output;
1678
    fn visit_complex(&mut self, _: Gc<SteelComplex>) -> Self::Output;
1679
    fn visit_char(&mut self, _: char) -> Self::Output;
1680
    fn visit_immutable_vector(&mut self, vector: SteelVector) -> Self::Output;
1681
    fn visit_void(&mut self) -> Self::Output;
1682
    fn visit_string(&mut self, string: SteelString) -> Self::Output;
1683
    fn visit_function_pointer(&mut self, ptr: FunctionSignature) -> Self::Output;
1684
    fn visit_symbol(&mut self, symbol: SteelString) -> Self::Output;
1685
    fn visit_custom_type(&mut self, custom_type: GcMut<Box<dyn CustomType>>) -> Self::Output;
1686
    fn visit_hash_map(&mut self, hashmap: SteelHashMap) -> Self::Output;
1687
    fn visit_hash_set(&mut self, hashset: SteelHashSet) -> Self::Output;
1688
    fn visit_steel_struct(&mut self, steel_struct: Gc<UserDefinedStruct>) -> Self::Output;
1689
    fn visit_port(&mut self, port: SteelPort) -> Self::Output;
1690
    fn visit_transducer(&mut self, transducer: Gc<Transducer>) -> Self::Output;
1691
    fn visit_reducer(&mut self, reducer: Gc<Reducer>) -> Self::Output;
1692
    fn visit_future_function(&mut self, function: BoxedAsyncFunctionSignature) -> Self::Output;
1693
    fn visit_future(&mut self, future: Gc<FutureResult>) -> Self::Output;
1694
    fn visit_stream(&mut self, stream: Gc<LazyStream>) -> Self::Output;
1695
    fn visit_boxed_function(&mut self, function: Gc<BoxedDynFunction>) -> Self::Output;
1696
    fn visit_continuation(&mut self, continuation: Continuation) -> Self::Output;
1697
    fn visit_list(&mut self, list: List<SteelVal>) -> Self::Output;
1698
    fn visit_mutable_function(&mut self, function: MutFunctionSignature) -> Self::Output;
1699
    fn visit_mutable_vector(&mut self, vector: HeapRef<Vec<SteelVal>>) -> Self::Output;
1700
    fn visit_builtin_function(&mut self, function: BuiltInSignature) -> Self::Output;
1701
    fn visit_boxed_iterator(&mut self, iterator: GcMut<OpaqueIterator>) -> Self::Output;
1702
    fn visit_syntax_object(&mut self, syntax_object: Gc<Syntax>) -> Self::Output;
1703
    fn visit_boxed_value(&mut self, boxed_value: GcMut<SteelVal>) -> Self::Output;
1704
    fn visit_reference_value(&mut self, reference: Gc<OpaqueReference<'static>>) -> Self::Output;
1705
    fn visit_heap_allocated(&mut self, heap_ref: HeapRef<SteelVal>) -> Self::Output;
1706
    fn visit_pair(&mut self, pair: Gc<Pair>) -> Self::Output;
1707
    fn visit_bytevector(&mut self, bytevector: SteelByteVector) -> Self::Output;
1708
}
1709

1710
pub trait BreadthFirstSearchSteelValReferenceVisitor<'a> {
1711
    type Output;
1712

1713
    fn default_output(&mut self) -> Self::Output;
1714

1715
    // TODO: Don't use the unsafe variant... if possible?
1716
    fn pop_front(&mut self) -> Option<*const SteelVal>;
1717

1718
    fn push_back(&mut self, value: &SteelVal);
1719

1720
    fn visit(&mut self) -> Self::Output {
×
1721
        let mut ret = self.default_output();
×
1722

1723
        while let Some(value) = self.pop_front() {
×
1724
            ret = match unsafe { &(*value) } {
×
1725
                Closure(c) => self.visit_closure(c),
×
1726
                BoolV(b) => self.visit_bool(*b),
×
1727
                NumV(n) => self.visit_float(*n),
×
1728
                IntV(i) => self.visit_int(*i),
×
1729
                Rational(x) => self.visit_rational(*x),
×
1730
                BigRational(x) => self.visit_bigrational(x),
×
1731
                Complex(_) => unimplemented!(),
×
1732
                CharV(c) => self.visit_char(*c),
×
1733
                VectorV(v) => self.visit_immutable_vector(v),
×
1734
                Void => self.visit_void(),
×
1735
                StringV(s) => self.visit_string(s),
×
1736
                FuncV(f) => self.visit_function_pointer(*f),
×
1737
                SymbolV(s) => self.visit_symbol(s),
×
1738
                SteelVal::Custom(c) => self.visit_custom_type(c),
×
1739
                HashMapV(h) => self.visit_hash_map(h),
×
1740
                HashSetV(s) => self.visit_hash_set(s),
×
1741
                CustomStruct(c) => self.visit_steel_struct(c),
×
1742
                PortV(p) => self.visit_port(p),
×
1743
                IterV(t) => self.visit_transducer(t),
×
1744
                ReducerV(r) => self.visit_reducer(r),
×
1745
                FutureFunc(f) => self.visit_future_function(f),
×
1746
                FutureV(f) => self.visit_future(f),
×
1747
                StreamV(s) => self.visit_stream(s),
×
1748
                BoxedFunction(b) => self.visit_boxed_function(b),
×
1749
                ContinuationFunction(c) => self.visit_continuation(c),
×
1750
                ListV(l) => self.visit_list(l),
×
1751
                MutFunc(m) => self.visit_mutable_function(m),
×
1752
                BuiltIn(b) => self.visit_builtin_function(b),
×
1753
                MutableVector(b) => self.visit_mutable_vector(b),
×
1754
                BoxedIterator(b) => self.visit_boxed_iterator(b),
×
1755
                SteelVal::SyntaxObject(s) => self.visit_syntax_object(s),
×
1756
                Boxed(b) => self.visit_boxed_value(b),
×
1757
                Reference(r) => self.visit_reference_value(r),
×
1758
                BigNum(b) => self.visit_bignum(b),
×
1759
                HeapAllocated(b) => self.visit_heap_allocated(b),
×
1760
                Pair(p) => self.visit_pair(p),
×
1761
                ByteVector(b) => self.visit_bytevector(b),
×
1762
            };
1763
        }
1764

1765
        ret
×
1766
    }
1767

1768
    fn visit_bytevector(&mut self, _: &'a SteelByteVector) -> Self::Output;
1769
    fn visit_closure(&mut self, _: &'a Gc<ByteCodeLambda>) -> Self::Output;
1770
    fn visit_bool(&mut self, _: bool) -> Self::Output;
1771
    fn visit_float(&mut self, _: f64) -> Self::Output;
1772
    fn visit_int(&mut self, _: isize) -> Self::Output;
1773
    fn visit_rational(&mut self, _: Rational32) -> Self::Output;
1774
    fn visit_bigrational(&mut self, _: &'a Gc<BigRational>) -> Self::Output;
1775
    fn visit_bignum(&mut self, _: &'a Gc<BigInt>) -> Self::Output;
1776
    fn visit_char(&mut self, _: char) -> Self::Output;
1777
    fn visit_immutable_vector(&mut self, vector: &'a SteelVector) -> Self::Output;
1778
    fn visit_void(&mut self) -> Self::Output;
1779
    fn visit_string(&mut self, string: &'a SteelString) -> Self::Output;
1780
    fn visit_function_pointer(&mut self, ptr: FunctionSignature) -> Self::Output;
1781
    fn visit_symbol(&mut self, symbol: &'a SteelString) -> Self::Output;
1782
    fn visit_custom_type(&mut self, custom_type: &'a GcMut<Box<dyn CustomType>>) -> Self::Output;
1783
    fn visit_hash_map(&mut self, hashmap: &'a SteelHashMap) -> Self::Output;
1784
    fn visit_hash_set(&mut self, hashset: &'a SteelHashSet) -> Self::Output;
1785
    fn visit_steel_struct(&mut self, steel_struct: &'a Gc<UserDefinedStruct>) -> Self::Output;
1786
    fn visit_port(&mut self, port: &'a SteelPort) -> Self::Output;
1787
    fn visit_transducer(&mut self, transducer: &'a Gc<Transducer>) -> Self::Output;
1788
    fn visit_reducer(&mut self, reducer: &'a Gc<Reducer>) -> Self::Output;
1789
    fn visit_future_function(&mut self, function: &'a BoxedAsyncFunctionSignature) -> Self::Output;
1790
    fn visit_future(&mut self, future: &'a Gc<FutureResult>) -> Self::Output;
1791
    fn visit_stream(&mut self, stream: &'a Gc<LazyStream>) -> Self::Output;
1792
    fn visit_boxed_function(&mut self, function: &'a Gc<BoxedDynFunction>) -> Self::Output;
1793
    fn visit_continuation(&mut self, continuation: &'a Continuation) -> Self::Output;
1794
    fn visit_list(&mut self, list: &'a List<SteelVal>) -> Self::Output;
1795
    fn visit_mutable_function(&mut self, function: &'a MutFunctionSignature) -> Self::Output;
1796
    fn visit_mutable_vector(&mut self, vector: &'a HeapRef<Vec<SteelVal>>) -> Self::Output;
1797
    fn visit_builtin_function(&mut self, function: &'a BuiltInSignature) -> Self::Output;
1798
    fn visit_boxed_iterator(&mut self, iterator: &'a GcMut<OpaqueIterator>) -> Self::Output;
1799
    fn visit_syntax_object(&mut self, syntax_object: &'a Gc<Syntax>) -> Self::Output;
1800
    fn visit_boxed_value(&mut self, boxed_value: &'a GcMut<SteelVal>) -> Self::Output;
1801
    fn visit_reference_value(
1802
        &mut self,
1803
        reference: &'a Gc<OpaqueReference<'static>>,
1804
    ) -> Self::Output;
1805
    fn visit_heap_allocated(&mut self, heap_ref: &'a HeapRef<SteelVal>) -> Self::Output;
1806
    fn visit_pair(&mut self, pair: &'a Gc<Pair>) -> Self::Output;
1807
}
1808

1809
#[cfg(feature = "sync")]
1810
pub(crate) trait BreadthFirstSearchSteelValReferenceVisitor2<'a> {
1811
    type Output;
1812

1813
    fn default_output(&mut self) -> Self::Output;
1814

1815
    fn pop_front(&mut self) -> Option<SteelValPointer>;
1816

1817
    fn push_back(&mut self, value: &SteelVal);
1818

1819
    fn visit(&mut self) -> Self::Output {
145✔
1820
        let mut ret = self.default_output();
435✔
1821

1822
        while let Some(value) = self.pop_front() {
73,752,383✔
1823
            ret = match value {
×
1824
                SteelValPointer::Closure(p) => self.visit_closure(unsafe { &(*p) }),
44,702✔
1825
                SteelValPointer::VectorV(p) => self.visit_immutable_vector(unsafe { &(*p) }),
×
1826
                SteelValPointer::Custom(p) => self.visit_custom_type(unsafe { &(*p) }),
19,728✔
1827
                SteelValPointer::HashMapV(p) => self.visit_hash_map(unsafe { &(*p) }),
6,536✔
1828
                SteelValPointer::HashSetV(p) => self.visit_hash_set(unsafe { &(*p) }),
344✔
1829
                SteelValPointer::CustomStruct(p) => self.visit_steel_struct(unsafe { &(*p) }),
757,408✔
1830
                SteelValPointer::IterV(p) => self.visit_transducer(unsafe { &(*p) }),
×
1831
                SteelValPointer::ReducerV(p) => self.visit_reducer(unsafe { &(*p) }),
×
1832
                SteelValPointer::StreamV(p) => self.visit_stream(unsafe { &(*p) }),
688✔
1833
                SteelValPointer::ContinuationFunction(p) => {
20✔
1834
                    self.visit_continuation(unsafe { &(*p) })
60✔
1835
                }
1836
                SteelValPointer::ListV(raw_cell) => self.visit_list(raw_cell),
258,084✔
1837
                SteelValPointer::Pair(p) => self.visit_pair(unsafe { &(*p) }),
100✔
1838
                SteelValPointer::MutableVector(heap_ref) => self.visit_mutable_vector(heap_ref),
144,771,860✔
1839
                SteelValPointer::SyntaxObject(p) => self.visit_syntax_object(unsafe { &(*p) }),
×
1840
                SteelValPointer::BoxedIterator(p) => self.visit_boxed_iterator(unsafe { &(*p) }),
×
1841
                SteelValPointer::Boxed(p) => self.visit_boxed_value(unsafe { &(*p) }),
×
1842
                SteelValPointer::HeapAllocated(heap_ref) => self.visit_heap_allocated(heap_ref),
1,510,840✔
1843
            };
1844
        }
1845

1846
        ret
145✔
1847
    }
1848

1849
    fn visit_closure(&mut self, _: &'a ByteCodeLambda) -> Self::Output;
1850
    fn visit_immutable_vector(&mut self, vector: &'a Vector<SteelVal>) -> Self::Output;
1851
    fn visit_custom_type(&mut self, custom_type: &'a RwLock<Box<dyn CustomType>>) -> Self::Output;
1852
    fn visit_hash_map(&mut self, hashmap: &'a crate::HashMap<SteelVal, SteelVal>) -> Self::Output;
1853
    fn visit_hash_set(&mut self, hashset: &'a crate::HashSet<SteelVal>) -> Self::Output;
1854
    fn visit_steel_struct(&mut self, steel_struct: &'a UserDefinedStruct) -> Self::Output;
1855
    fn visit_transducer(&mut self, transducer: &'a Transducer) -> Self::Output;
1856
    fn visit_reducer(&mut self, reducer: &'a Reducer) -> Self::Output;
1857
    fn visit_stream(&mut self, stream: &'a LazyStream) -> Self::Output;
1858
    fn visit_continuation(&mut self, continuation: &'a RwLock<ContinuationMark>) -> Self::Output;
1859
    fn visit_list(&mut self, list: crate::values::lists::CellPointer<SteelVal>) -> Self::Output;
1860
    fn visit_mutable_vector(&mut self, vector: HeapRef<Vec<SteelVal>>) -> Self::Output;
1861
    fn visit_boxed_iterator(&mut self, iterator: &'a RwLock<OpaqueIterator>) -> Self::Output;
1862
    fn visit_syntax_object(&mut self, syntax_object: &'a Syntax) -> Self::Output;
1863
    fn visit_boxed_value(&mut self, boxed_value: &'a RwLock<SteelVal>) -> Self::Output;
1864
    fn visit_heap_allocated(&mut self, heap_ref: HeapRef<SteelVal>) -> Self::Output;
1865
    fn visit_pair(&mut self, pair: &'a Pair) -> Self::Output;
1866
}
1867

1868
thread_local! {
1869
    static LEFT_QUEUE: RefCell<Vec<SteelVal>> = RefCell::new(Vec::with_capacity(128));
1870
    static RIGHT_QUEUE: RefCell<Vec<SteelVal>> = RefCell::new(Vec::with_capacity(128));
1871
    static VISITED_SET: RefCell<fxhash::FxHashSet<(usize, usize)>> = RefCell::new(fxhash::FxHashSet::default());
1872
    static EQ_DEPTH: Cell<usize> = const { Cell::new(0) };
1873
}
1874

1875
fn increment_eq_depth() {
×
1876
    #[cfg(feature = "sandbox")]
1877
    EQ_DEPTH.with(|x| x.set(x.get() + 1));
1878
}
1879

1880
fn decrement_eq_depth() {
×
1881
    #[cfg(feature = "sandbox")]
1882
    EQ_DEPTH.with(|x| x.set(x.get() - 1));
1883
}
1884

1885
fn reset_eq_depth() {
3,099,668✔
1886
    #[cfg(feature = "sandbox")]
1887
    EQ_DEPTH.with(|x| x.set(0));
1888
}
1889

1890
fn eq_depth() -> usize {
39✔
1891
    #[cfg(feature = "sandbox")]
1892
    return EQ_DEPTH.with(|x| x.get());
1893

1894
    #[cfg(not(feature = "sandbox"))]
1895
    0
39✔
1896
}
1897

1898
struct RecursiveEqualityHandler<'a> {
1899
    left: EqualityVisitor<'a>,
1900
    right: EqualityVisitor<'a>,
1901
    visited: &'a mut fxhash::FxHashSet<(usize, usize)>,
1902
}
1903

1904
impl<'a> RecursiveEqualityHandler<'a> {
1905
    pub fn compare_equality(&mut self, left: SteelVal, right: SteelVal) -> bool {
3,099,668✔
1906
        self.left.push_back(left);
9,299,004✔
1907
        self.right.push_back(right);
9,299,004✔
1908

1909
        self.visit()
6,199,336✔
1910
    }
1911

1912
    fn should_visit(&mut self, value: (usize, usize)) -> bool {
6,026,464✔
1913
        // if !self.found_mutable_object {
1914
        // return true;
1915
        // }
1916

1917
        if self.visited.insert(value) {
18,079,392✔
1918
            return true;
6,026,459✔
1919
        }
1920

NEW
1921
        false
×
1922
    }
1923

1924
    fn visit(&mut self) -> bool {
3,099,668✔
1925
        loop {
×
1926
            let (left, right) = match (self.left.pop_front(), self.right.pop_front()) {
60,687,048✔
1927
                (Some(l), Some(r)) => (l, r),
24,339,114✔
1928
                (None, None) => return true,
3,002,205✔
1929
                _ => return false,
×
1930
            };
1931

1932
            // println!("{} - {}", left, right);
1933

1934
            // println!(
1935
            //     "Queue size: {:?}",
1936
            //     self.left.queue.len(),
1937
            //     // self.right.queue.len()
1938
            // );
1939

1940
            match (left, right) {
16,226,076✔
1941
                (ListV(l), ListV(r)) => {
6,020,004✔
1942
                    // If we've reached the same object, we're good
1943
                    if l.ptr_eq(&r) || l.storage_ptr_eq(&r) {
12,038,633✔
1944
                        continue;
1,375✔
1945
                    }
1946

1947
                    if self.should_visit(l.identity_tuple())
×
1948
                        && self.should_visit(r.identity_tuple())
12,034,508✔
1949
                    {
1950
                        if l.len() != r.len() {
9,025,866✔
1951
                            return false;
10,102✔
1952
                        }
1953

1954
                        for (lvalue, rvalue) in l.iter().zip(r.iter()) {
7,921,912✔
1955
                            // TODO: @Matt - need to do optimistic checks here so we don't
1956
                            // visit things we don't need to - basically a "check left" function
1957
                            match (lvalue, rvalue) {
×
1958
                                (SteelVal::ListV(llist), SteelVal::ListV(rlist))
5,843,658✔
1959
                                    if (llist.is_empty() && rlist.is_empty())
5,908,694✔
1960
                                        || llist.ptr_eq(rlist)
2,889,311✔
1961
                                        || llist.storage_ptr_eq(rlist) =>
2,921,829✔
1962
                                {
1963
                                    continue;
×
1964
                                }
1965
                                // (SteelVal::ListV(llist), SteelVal::ListV(rlist)) if llist.len() == 1 && rlist.len() == 1 {
1966

1967
                                // }
1968
                                (a, b) => {
5,005,238✔
1969
                                    // println!("Pushing back: {}", a);
1970

1971
                                    self.left.push_back(a.clone());
×
1972
                                    self.right.push_back(b.clone());
×
1973
                                }
1974
                            }
1975
                        }
1976
                    }
1977

1978
                    continue;
2,998,525✔
1979
                }
1980
                // If we run into issues with any stack overflow
1981
                // check here first.
1982
                (Pair(l), Pair(r)) => {
10,024✔
1983
                    if Gc::ptr_eq(&l, &r) {
15,036✔
1984
                        continue;
2,500✔
1985
                    }
1986

1987
                    if self.should_visit((l.as_ptr() as usize, 0))
×
1988
                        && self.should_visit((r.as_ptr() as usize, 0))
7,536✔
1989
                    {
1990
                        self.left.push_back(l.car());
10,048✔
1991
                        self.right.push_back(r.car());
10,048✔
1992

1993
                        self.left.push_back(l.cdr());
10,048✔
1994
                        self.right.push_back(r.cdr());
7,536✔
1995
                    }
1996

1997
                    continue;
×
1998
                }
1999
                (BoolV(l), BoolV(r)) => {
44,988✔
2000
                    if l != r {
×
2001
                        return false;
×
2002
                    }
2003

2004
                    continue;
×
2005
                }
2006
                (NumV(l), NumV(r)) => {
×
2007
                    if l != r {
×
2008
                        return false;
×
2009
                    }
2010

2011
                    continue;
×
2012
                }
2013
                (IntV(l), IntV(r)) => {
243,762✔
2014
                    if l != r {
121,881✔
2015
                        return false;
6✔
2016
                    }
2017

2018
                    continue;
×
2019
                }
2020
                (CharV(l), CharV(r)) => {
526✔
2021
                    if l != r {
263✔
2022
                        return false;
246✔
2023
                    }
2024

2025
                    continue;
×
2026
                }
2027
                (VectorV(l), VectorV(r)) => {
82✔
2028
                    if l.len() != r.len() {
82✔
2029
                        return false;
×
2030
                    }
2031

2032
                    // If these point to the same object, break early
2033
                    if Gc::ptr_eq(&l.0, &r.0) {
×
2034
                        continue;
×
2035
                    }
2036

2037
                    // Should we visit these?
2038
                    if self.should_visit((l.0.as_ptr() as usize, 0))
×
2039
                        && self.should_visit((r.0.as_ptr() as usize, 0))
123✔
2040
                    {
2041
                        self.left.visit_immutable_vector(l);
164✔
2042
                        self.right.visit_immutable_vector(r);
82✔
2043
                    } else {
2044
                        return false;
×
2045
                    }
2046

2047
                    continue;
41✔
2048
                }
2049

2050
                (VectorV(l), MutableVector(r)) => {
96✔
2051
                    if l.len() != r.borrow(|x| x.len()) {
240✔
2052
                        return false;
×
2053
                    }
2054

2055
                    self.left.visit_immutable_vector(l);
×
2056
                    self.right.visit_mutable_vector(r);
×
2057

2058
                    continue;
×
2059
                }
2060
                (MutableVector(l), VectorV(r)) => {
×
2061
                    if l.borrow(|x| x.len()) != r.len() {
×
2062
                        return false;
×
2063
                    }
2064

2065
                    self.left.visit_mutable_vector(l);
×
2066
                    self.right.visit_immutable_vector(r);
×
2067

2068
                    continue;
×
2069
                }
2070
                (Void, Void) => {
×
2071
                    continue;
×
2072
                }
2073
                (StringV(l), StringV(r)) => {
15,468✔
2074
                    if l != r {
7,734✔
2075
                        return false;
×
2076
                    }
2077
                    continue;
×
2078
                }
2079
                (FuncV(l), FuncV(r)) => {
×
2080
                    if l as usize != r as usize {
×
2081
                        return false;
×
2082
                    }
2083
                    continue;
×
2084
                }
2085
                (MutFunc(l), MutFunc(r)) => {
×
2086
                    if l as usize != r as usize {
×
2087
                        return false;
×
2088
                    }
2089
                    continue;
×
2090
                }
2091
                (SymbolV(l), SymbolV(r)) => {
9,662,572✔
2092
                    if l != r {
4,831,286✔
2093
                        return false;
12✔
2094
                    }
2095
                    continue;
×
2096
                }
2097
                (SteelVal::Custom(l), SteelVal::Custom(r)) => {
×
2098
                    if l.read().inner_type_id() != r.read().inner_type_id() {
×
2099
                        return false;
×
2100
                    }
2101

2102
                    if l.read().check_equality_hint(r.read().as_ref()) {
×
2103
                        // Go down to the next level
2104
                        self.left.visit_custom_type(l);
×
2105
                        self.right.visit_custom_type(r);
×
2106
                        continue;
×
2107
                    } else {
2108
                        return false;
×
2109
                    }
2110
                }
2111

2112
                (SteelVal::Custom(l), other) => {
×
2113
                    if l.read().check_equality_hint_general(&other) {
×
2114
                        self.left.visit_custom_type(l);
×
2115

2116
                        match other {
×
2117
                            Closure(x) => self.right.visit_closure(x),
×
2118
                            VectorV(v) => self.right.visit_immutable_vector(v),
×
2119
                            SteelVal::Custom(r) => self.right.visit_custom_type(r),
×
2120
                            HashMapV(r) => self.right.visit_hash_map(r),
×
2121
                            HashSetV(r) => self.right.visit_hash_set(r),
×
2122
                            CustomStruct(r) => self.right.visit_steel_struct(r),
×
2123
                            PortV(r) => self.right.visit_port(r),
×
2124
                            IterV(r) => self.right.visit_transducer(r),
×
2125
                            ReducerV(r) => self.right.visit_reducer(r),
×
2126
                            ContinuationFunction(r) => self.right.visit_continuation(r),
×
2127
                            ListV(r) => self.right.visit_list(r),
×
2128
                            SteelVal::Pair(r) => self.right.visit_pair(r),
×
2129
                            MutableVector(r) => self.right.visit_mutable_vector(r),
×
2130
                            BoxedIterator(r) => self.right.visit_boxed_iterator(r),
×
2131
                            SteelVal::SyntaxObject(r) => self.right.visit_syntax_object(r),
×
2132
                            Boxed(r) => self.right.visit_boxed_value(r),
×
2133
                            HeapAllocated(r) => self.right.visit_heap_allocated(r),
×
2134
                            _ => {}
×
2135
                        }
2136

2137
                        continue;
×
2138
                    } else {
2139
                        return false;
×
2140
                    }
2141
                }
2142

2143
                (other, SteelVal::Custom(r)) => {
2✔
2144
                    if r.read().check_equality_hint_general(&other) {
2✔
2145
                        match other {
×
2146
                            Closure(x) => self.left.visit_closure(x),
×
2147
                            VectorV(v) => self.left.visit_immutable_vector(v),
×
2148
                            SteelVal::Custom(r) => self.left.visit_custom_type(r),
×
2149
                            HashMapV(r) => self.left.visit_hash_map(r),
×
2150
                            HashSetV(r) => self.left.visit_hash_set(r),
×
2151
                            CustomStruct(r) => self.left.visit_steel_struct(r),
×
2152
                            PortV(r) => self.left.visit_port(r),
×
2153
                            IterV(r) => self.left.visit_transducer(r),
×
2154
                            ReducerV(r) => self.left.visit_reducer(r),
×
2155
                            ContinuationFunction(r) => self.left.visit_continuation(r),
×
2156
                            ListV(r) => self.left.visit_list(r),
×
2157
                            SteelVal::Pair(r) => self.left.visit_pair(r),
×
2158
                            MutableVector(r) => self.left.visit_mutable_vector(r),
×
2159
                            BoxedIterator(r) => self.left.visit_boxed_iterator(r),
×
2160
                            SteelVal::SyntaxObject(r) => self.left.visit_syntax_object(r),
×
2161
                            Boxed(r) => self.left.visit_boxed_value(r),
×
2162
                            HeapAllocated(r) => self.left.visit_heap_allocated(r),
×
2163
                            _ => {}
×
2164
                        }
2165

2166
                        self.right.visit_custom_type(r);
×
2167

2168
                        continue;
×
2169
                    } else {
2170
                        return false;
1✔
2171
                    }
2172
                }
2173

2174
                (SteelVal::HashMapV(l), SteelVal::HashMapV(r)) => {
70✔
2175
                    if Gc::ptr_eq(&l.0, &r.0) {
105✔
2176
                        continue;
×
2177
                    }
2178

2179
                    if self.should_visit((l.0.as_ptr() as usize, 0))
×
2180
                        && self.should_visit((r.0.as_ptr() as usize, 0))
105✔
2181
                    {
2182
                        if l.len() != r.len() {
70✔
2183
                            return false;
×
2184
                        }
2185

2186
                        // TODO: Implicitly here we are assuming that this key was even hashable
2187
                        // to begin with, since it ended up in the right spot, and didn't blow
2188
                        // the stack on a recursive structure.
2189
                        //
2190
                        // This still does not handle the pathological edge case of something like
2191
                        // (hash (hash (hash ...) value) value)
2192
                        //
2193
                        // In this case, we'll get a stack overflow, when trying to compare equality
2194
                        // with these maps if they're sufficiently deep.
2195
                        //
2196
                        // The issue is that if the two maps are equivalent, we need to check the
2197
                        // existence of each key in the left map with each key in the right map.
2198
                        // Doing so invokes an equality check, where we'll then invoke this logic
2199
                        // again. We could solve this by disallowing hashmaps as keys - then
2200
                        // we would not the same issue where putting a hashmap into the map
2201
                        // causes the equality checks to go off the rails.
2202

2203
                        if eq_depth() > 512 {
×
2204
                            log::error!("Aborting eq checks before the stack overflows");
×
2205

2206
                            return false;
×
2207
                        }
2208

2209
                        for (key, value) in l.0.iter() {
150✔
2210
                            if let Some(right_value) = r.0.get(key) {
225✔
2211
                                self.left.push_back(value.clone());
×
2212
                                self.right.push_back(right_value.clone());
×
2213
                            } else {
2214
                                // We know that these are not equal, because the
2215
                                // key in the left map does not exist in the right
2216
                                return false;
×
2217
                            }
2218
                        }
2219
                    }
2220

2221
                    continue;
35✔
2222
                }
2223
                (HashSetV(l), HashSetV(r)) => {
8✔
2224
                    if Gc::ptr_eq(&l.0, &r.0) {
12✔
2225
                        continue;
×
2226
                    }
2227

2228
                    if self.should_visit((l.0.as_ptr() as usize, 0))
×
2229
                        && self.should_visit((r.0.as_ptr() as usize, 0))
12✔
2230
                    {
2231
                        if l.len() != r.len() {
8✔
2232
                            return false;
×
2233
                        }
2234
                        if eq_depth() > 512 {
×
2235
                            log::error!("Aborting eq checks before the stack overflows");
×
2236

2237
                            return false;
×
2238
                        }
2239

2240
                        for key in l.0.iter() {
11✔
2241
                            if !l.0.contains(key) {
22✔
2242
                                return false;
×
2243
                            }
2244
                        }
2245
                    }
2246

2247
                    continue;
4✔
2248
                }
2249
                (CustomStruct(l), CustomStruct(r)) => {
5,016✔
2250
                    // If these are the same object, just continue
2251
                    if Gc::ptr_eq(&l, &r) {
7,524✔
2252
                        continue;
2,499✔
2253
                    }
2254

2255
                    if self.should_visit((l.as_ptr() as usize, 0))
×
2256
                        && self.should_visit((r.as_ptr() as usize, 0))
27✔
2257
                    {
2258
                        // Check the top level equality indicators to make sure
2259
                        // that these two types are the same
2260
                        if !(l.type_descriptor == r.type_descriptor && l.name() == r.name()) {
27✔
2261
                            return false;
×
2262
                        }
2263

2264
                        self.left.visit_steel_struct(l);
27✔
2265
                        self.right.visit_steel_struct(r);
27✔
2266
                    }
2267

2268
                    continue;
9✔
2269
                }
2270
                // (PortV(_), PortV(_)) => {
2271
                // return
2272
                // }
2273
                (IterV(l), IterV(r)) => {
×
2274
                    self.left.visit_transducer(l);
×
2275
                    self.right.visit_transducer(r);
×
2276

2277
                    continue;
×
2278
                }
2279
                (ReducerV(l), ReducerV(r)) => {
×
2280
                    self.left.visit_reducer(l);
×
2281
                    self.right.visit_reducer(r);
×
2282

2283
                    continue;
×
2284
                }
2285
                // FutureV(f) => self.visit_future(f),
2286
                (ContinuationFunction(l), ContinuationFunction(r)) => {
×
2287
                    if !Continuation::ptr_eq(&l, &r) {
×
2288
                        return false;
×
2289
                    }
2290

2291
                    continue;
×
2292
                }
2293
                // MutFunc(m) => self.visit_mutable_function(m),
2294
                (BuiltIn(l), BuiltIn(r)) => {
×
2295
                    if l as usize != r as usize {
×
2296
                        return false;
×
2297
                    }
2298
                    continue;
×
2299
                }
2300
                // MutableVector(b) => self.visit_mutable_vector(b),
2301
                // BoxedIterator(b) => self.visit_boxed_iterator(b),
2302
                // SteelVal::SyntaxObject(s) => self.visit_syntax_object(s),
2303
                // Boxed(b) => self.visit_boxed_value(b),
2304
                // Reference(r) => self.visit_reference_value(r),
2305
                (BigNum(l), BigNum(r)) => {
44✔
2306
                    if l != r {
22✔
2307
                        return false;
×
2308
                    }
2309
                    continue;
×
2310
                }
2311
                (SyntaxObject(l), SyntaxObject(r)) => {
×
2312
                    if Gc::ptr_eq(&l, &r) {
×
2313
                        continue;
×
2314
                    }
2315

2316
                    self.left.visit_syntax_object(l);
×
2317
                    self.right.visit_syntax_object(r);
×
2318

2319
                    continue;
×
2320
                }
2321
                (HeapAllocated(l), HeapAllocated(r)) => {
×
2322
                    if HeapRef::ptr_eq(&l, &r) {
×
2323
                        continue;
×
2324
                    }
2325

2326
                    self.left.visit_heap_allocated(l);
×
2327
                    self.right.visit_heap_allocated(r);
×
2328

2329
                    continue;
×
2330
                }
2331

2332
                (MutableVector(l), MutableVector(r)) => {
4,200✔
2333
                    if HeapRef::ptr_eq(&l, &r) {
6,300✔
2334
                        continue;
96✔
2335
                    }
2336

2337
                    if self.should_visit((l.as_ptr_usize(), 0))
×
2338
                        && self.should_visit((r.as_ptr_usize(), 0))
6,012✔
2339
                    {
2340
                        if l.borrow(|x| x.len()) != r.borrow(|x| x.len()) {
16,032✔
2341
                            return false;
×
2342
                        }
2343

2344
                        self.left.visit_mutable_vector(l);
×
2345
                        self.right.visit_mutable_vector(r);
×
2346
                    }
2347

2348
                    continue;
2,004✔
2349
                }
2350
                (Closure(l), Closure(r)) => {
189✔
2351
                    if l != r {
×
2352
                        return false;
172✔
2353
                    }
2354

2355
                    self.left.visit_closure(l);
×
2356
                    self.right.visit_closure(r);
×
2357

2358
                    continue;
×
2359
                }
2360
                (_, _) => {
×
2361
                    return false;
86,924✔
2362
                }
2363
            }
2364

2365
            // unreachable!();
2366
        }
2367
    }
2368
}
2369

2370
// TODO: This _needs_ to use references. Or otherwise we'll thrash stuff on drop
2371
pub struct EqualityVisitor<'a> {
2372
    // Mark each node that we've visited, if we encounter any mutable objects
2373
    // on the way, then we'll start using the visited set. But we'll optimistically
2374
    // assume that there are no mutable objects, and we won't start using this
2375
    // until we absolutely have to.
2376
    // found_mutable_object: bool,
2377
    queue: &'a mut Vec<SteelVal>,
2378
}
2379

2380
impl<'a> BreadthFirstSearchSteelValVisitor for EqualityVisitor<'a> {
2381
    type Output = ();
2382

2383
    fn default_output(&mut self) -> Self::Output {}
×
2384

2385
    fn pop_front(&mut self) -> Option<SteelVal> {
22,230,486✔
2386
        self.queue.pop()
44,460,972✔
2387
    }
2388

2389
    fn push_back(&mut self, value: SteelVal) {
16,236,688✔
2390
        self.queue.push(value)
48,710,064✔
2391
    }
2392

2393
    fn visit_closure(&mut self, _closure: Gc<ByteCodeLambda>) -> Self::Output {}
68✔
2394

2395
    // Leaf nodes, we don't need to do anything here
2396
    fn visit_bytevector(&mut self, _bytevector: SteelByteVector) -> Self::Output {}
×
2397
    fn visit_bool(&mut self, _boolean: bool) -> Self::Output {}
×
2398
    fn visit_float(&mut self, _float: f64) -> Self::Output {}
×
2399
    fn visit_int(&mut self, _int: isize) -> Self::Output {}
×
2400
    fn visit_rational(&mut self, _: Rational32) -> Self::Output {}
×
2401
    fn visit_bigrational(&mut self, _: Gc<BigRational>) -> Self::Output {}
×
2402
    fn visit_bignum(&mut self, _: Gc<BigInt>) -> Self::Output {}
×
2403
    fn visit_complex(&mut self, _: Gc<SteelComplex>) -> Self::Output {}
×
2404
    fn visit_char(&mut self, _c: char) -> Self::Output {}
×
2405
    fn visit_void(&mut self) -> Self::Output {}
×
2406
    fn visit_string(&mut self, _string: SteelString) -> Self::Output {}
×
2407
    fn visit_function_pointer(&mut self, _ptr: FunctionSignature) -> Self::Output {}
×
2408
    fn visit_symbol(&mut self, _symbol: SteelString) -> Self::Output {}
×
2409
    fn visit_port(&mut self, _port: SteelPort) -> Self::Output {}
×
2410
    fn visit_boxed_function(&mut self, _function: Gc<BoxedDynFunction>) -> Self::Output {}
×
2411
    fn visit_mutable_function(&mut self, _function: MutFunctionSignature) -> Self::Output {}
×
2412
    fn visit_builtin_function(&mut self, _function: BuiltInSignature) -> Self::Output {}
×
2413

2414
    //
2415
    fn visit_immutable_vector(&mut self, vector: SteelVector) -> Self::Output {
130✔
2416
        // If we've found the mutable object, mark that this has been visited. Only
2417
        // if self.should_visit(vector.0.as_ptr() as usize) {
2418
        for value in vector.iter() {
673✔
2419
            self.push_back(value.clone());
×
2420
        }
2421
        // }
2422
    }
2423

2424
    // SHOULD SET MUTABLE HERE
2425
    fn visit_custom_type(&mut self, custom_type: GcMut<Box<dyn CustomType>>) -> Self::Output {
×
2426
        custom_type.read().visit_children_for_equality(self);
×
2427
    }
2428

2429
    fn visit_hash_map(&mut self, _hashmap: SteelHashMap) -> Self::Output {
×
2430
        // TODO: See comment above
2431
    }
2432

2433
    fn visit_hash_set(&mut self, _hashset: SteelHashSet) -> Self::Output {
×
2434
        // TODO: See comment above
2435
    }
2436

2437
    fn visit_steel_struct(&mut self, steel_struct: Gc<UserDefinedStruct>) -> Self::Output {
18✔
2438
        // if self.should_visit(steel_struct.as_ptr() as usize) {
2439
        for value in steel_struct.fields.iter() {
58✔
2440
            self.push_back(value.clone());
×
2441
        }
2442
        // }
2443
    }
2444

2445
    fn visit_transducer(&mut self, transducer: Gc<Transducer>) -> Self::Output {
×
2446
        for transducer in transducer.ops.iter() {
×
2447
            match transducer.clone() {
×
2448
                crate::values::transducers::Transducers::Map(m) => self.push_back(m),
×
2449
                crate::values::transducers::Transducers::Filter(v) => self.push_back(v),
×
2450
                crate::values::transducers::Transducers::Take(t) => self.push_back(t),
×
2451
                crate::values::transducers::Transducers::Drop(d) => self.push_back(d),
×
2452
                crate::values::transducers::Transducers::FlatMap(fm) => self.push_back(fm),
×
2453
                crate::values::transducers::Transducers::Flatten => {}
×
2454
                crate::values::transducers::Transducers::Window(w) => self.push_back(w),
×
2455
                crate::values::transducers::Transducers::TakeWhile(tw) => self.push_back(tw),
×
2456
                crate::values::transducers::Transducers::DropWhile(dw) => self.push_back(dw),
×
2457
                crate::values::transducers::Transducers::Extend(e) => self.push_back(e),
×
2458
                crate::values::transducers::Transducers::Cycle => {}
×
2459
                crate::values::transducers::Transducers::Enumerating => {}
×
2460
                crate::values::transducers::Transducers::Zipping(z) => self.push_back(z),
×
2461
                crate::values::transducers::Transducers::Interleaving(i) => self.push_back(i),
×
2462
            }
2463
        }
2464
    }
2465

2466
    fn visit_reducer(&mut self, reducer: Gc<Reducer>) -> Self::Output {
×
2467
        match reducer.as_ref().clone() {
×
2468
            Reducer::ForEach(f) => self.push_back(f),
×
2469
            Reducer::Generic(rf) => {
×
2470
                self.push_back(rf.initial_value);
×
2471
                self.push_back(rf.function);
×
2472
            }
2473
            _ => {}
×
2474
        }
2475
    }
2476

2477
    fn visit_future_function(&mut self, _function: BoxedAsyncFunctionSignature) -> Self::Output {}
×
2478
    fn visit_future(&mut self, _future: Gc<FutureResult>) -> Self::Output {}
×
2479

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

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

2484
    fn visit_list(&mut self, list: List<SteelVal>) -> Self::Output {
×
2485
        for value in list.iter() {
×
2486
            self.push_back(value.clone());
×
2487
        }
2488
    }
2489

2490
    fn visit_mutable_vector(&mut self, vector: HeapRef<Vec<SteelVal>>) -> Self::Output {
4,056✔
2491
        vector.borrow(|x| {
12,168✔
2492
            for value in x.iter() {
24,355✔
2493
                self.push_back(value.clone());
×
2494
            }
2495
        })
2496
    }
2497

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

2500
    fn visit_syntax_object(&mut self, syntax_object: Gc<Syntax>) -> Self::Output {
×
2501
        if let Some(raw) = syntax_object.raw.clone() {
×
2502
            self.push_back(raw);
×
2503
        }
2504

2505
        self.push_back(syntax_object.syntax.clone());
×
2506
    }
2507

2508
    fn visit_boxed_value(&mut self, boxed_value: GcMut<SteelVal>) -> Self::Output {
×
2509
        self.push_back(boxed_value.read().clone());
×
2510
    }
2511

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

2514
    // Should set mutable here
2515
    fn visit_heap_allocated(&mut self, heap_ref: HeapRef<SteelVal>) -> Self::Output {
×
2516
        // self.found_mutable_object = true;
2517

2518
        // if self.should_visit(heap_ref.as_ptr_usize()) {
2519
        self.push_back(heap_ref.get());
×
2520
        // }
2521
    }
2522

2523
    fn visit_pair(&mut self, pair: Gc<Pair>) -> Self::Output {
×
2524
        self.push_back(pair.car());
×
2525
        self.push_back(pair.cdr());
×
2526
    }
2527
}
2528

2529
impl PartialEq for SteelVal {
2530
    fn eq(&self, other: &Self) -> bool {
10,787,762✔
2531
        match (self, other) {
21,575,524✔
2532
            (Void, Void) => true,
29✔
2533
            (BoolV(l), BoolV(r)) => l == r,
84,976✔
2534
            (IntV(l), IntV(r)) => l == r,
1,351,947✔
2535
            (NumV(l), NumV(r)) => l == r,
1,281✔
2536
            (Rational(l), Rational(r)) => l == r,
297✔
2537
            (BigRational(l), BigRational(r)) => l == r,
36✔
2538
            (BigNum(l), BigNum(r)) => l == r,
165✔
2539
            (Complex(l), Complex(r)) => l == r,
201✔
2540
            (StringV(l), StringV(r)) => l == r,
148,710✔
2541
            (SymbolV(l), SymbolV(r)) => l == r,
21,189,480✔
2542
            (CharV(l), CharV(r)) => l == r,
117,093✔
2543
            (FuncV(l), FuncV(r)) => *l as usize == *r as usize,
×
2544
            (ByteVector(l), ByteVector(r)) => l == r,
57✔
2545
            // (VectorV(l), VectorV(r)) => l == r,
2546
            // (ListV(l), ListV(r)) => l == r,
2547
            // (HashSetV(l), HashSetV(r)) => l == r,
2548
            // (HashMapV(l), HashMapV(r)) => l == r,
2549
            // (Closure(l), Closure(r)) => l == r,
2550
            // (IterV(l), IterV(r)) => l == r,
2551
            // (ListV(l), ListV(r)) => l == r,
2552
            // (CustomStruct(l), CustomStruct(r)) => l == r,
2553
            // (Custom(l), Custom(r)) => Gc::ptr_eq(l, r),
2554
            // (HeapAllocated(l), HeapAllocated(r)) => l.get() == r.get(),
2555
            (left, right) => {
6,199,336✔
2556
                LEFT_QUEUE.with(|left_queue| {
9,299,004✔
2557
                    RIGHT_QUEUE.with(|right_queue| {
9,299,004✔
2558
                        VISITED_SET.with(|visited_set| {
9,299,004✔
2559
                            match (
2560
                                left_queue.try_borrow_mut(),
6,199,336✔
2561
                                right_queue.try_borrow_mut(),
6,199,336✔
2562
                                visited_set.try_borrow_mut(),
6,199,336✔
2563
                            ) {
2564
                                (Ok(mut left_queue), Ok(mut right_queue), Ok(mut visited_set)) => {
3,099,668✔
2565
                                    let mut equality_handler = RecursiveEqualityHandler {
2566
                                        left: EqualityVisitor {
2567
                                            queue: &mut left_queue,
2568
                                        },
2569
                                        right: EqualityVisitor {
2570
                                            queue: &mut right_queue,
2571
                                        },
2572
                                        visited: &mut visited_set,
2573
                                    };
2574

2575
                                    let res = equality_handler
2576
                                        .compare_equality(left.clone(), right.clone());
2577

2578
                                    reset_eq_depth();
2579

2580
                                    // Clean up!
2581
                                    equality_handler.left.queue.clear();
2582
                                    equality_handler.right.queue.clear();
2583
                                    equality_handler.visited.clear();
2584

2585
                                    res
2586
                                }
2587
                                _ => {
2588
                                    let mut left_queue = Vec::new();
×
2589
                                    let mut right_queue = Vec::new();
×
2590

2591
                                    let mut visited_set = fxhash::FxHashSet::default();
×
2592

2593
                                    increment_eq_depth();
×
2594

2595
                                    let mut equality_handler = RecursiveEqualityHandler {
×
2596
                                        left: EqualityVisitor {
×
2597
                                            queue: &mut left_queue,
×
2598
                                        },
2599
                                        right: EqualityVisitor {
×
2600
                                            queue: &mut right_queue,
×
2601
                                        },
2602
                                        visited: &mut visited_set,
×
2603
                                    };
2604

2605
                                    let res = equality_handler
×
2606
                                        .compare_equality(left.clone(), right.clone());
×
2607

2608
                                    decrement_eq_depth();
×
2609

2610
                                    res
×
2611
                                }
2612
                            }
2613
                        })
2614
                    })
2615
                })
2616
            }
2617
        }
2618
    }
2619
}
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