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

mattwparas / steel / 11924200389

20 Nov 2024 12:20AM UTC coverage: 46.725% (-0.2%) from 46.956%
11924200389

Pull #290

github

web-flow
Merge faebbd075 into a6f7286de
Pull Request #290: Fix provides within macros

37 of 263 new or added lines in 7 files covered. (14.07%)

27 existing lines in 11 files now uncovered.

12469 of 26686 relevant lines covered (46.72%)

483326.6 hits per line

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

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

7
use super::*;
8

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

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

18
    depth: usize,
19
}
20

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

313
        self.depth -= 1;
314

315
        res
316
    }
317
}
318

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

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

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

334
impl Custom for SteelCycleCollector {}
335

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

458
            return true;
28✔
459
        }
460

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

623
#[cfg(not(feature = "without-drop-protection"))]
624
pub(crate) mod drop_impls {
625
    use super::*;
626

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

632
    impl Drop for SteelVector {
633
        fn drop(&mut self) {
637✔
634
            if self.0.is_empty() {
637✔
635
                return;
134✔
636
            }
637

638
            if let Some(inner) = self.0.get_mut() {
444✔
639
                DROP_BUFFER
222✔
640
                    .try_with(|drop_buffer| {
444✔
641
                        if let Ok(mut drop_buffer) = drop_buffer.try_borrow_mut() {
444✔
642
                            for value in std::mem::take(inner) {
5,608✔
643
                                drop_buffer.push_back(value);
2,804✔
644
                            }
645

646
                            IterativeDropHandler::bfs(&mut drop_buffer);
222✔
647
                        }
648
                    })
649
                    .ok();
650
            }
651
        }
652
    }
653

654
    impl Drop for SteelHashMap {
655
        fn drop(&mut self) {
230,935✔
656
            if self.0.is_empty() {
230,935✔
657
                return;
101,846✔
658
            }
659

660
            if let Some(inner) = self.0.get_mut() {
6,510✔
661
                DROP_BUFFER
3,255✔
662
                    .try_with(|drop_buffer| {
6,510✔
663
                        if let Ok(mut drop_buffer) = drop_buffer.try_borrow_mut() {
6,510✔
664
                            for (key, value) in std::mem::take(inner) {
37,168✔
665
                                drop_buffer.push_back(key);
18,584✔
666
                                drop_buffer.push_back(value);
18,584✔
667
                            }
668

669
                            IterativeDropHandler::bfs(&mut drop_buffer);
3,255✔
670
                        }
671
                    })
672
                    .ok();
673
            }
674
        }
675
    }
676

677
    impl Drop for UserDefinedStruct {
678
        fn drop(&mut self) {
106,500✔
679
            if self.fields.is_empty() {
106,500✔
680
                return;
4,450✔
681
            }
682

683
            if DROP_BUFFER
102,050✔
684
                .try_with(|drop_buffer| {
204,100✔
685
                    if let Ok(mut drop_buffer) = drop_buffer.try_borrow_mut() {
204,039✔
686
                        // for value in std::mem::take(&mut self.fields) {
687
                        //     drop_buffer.push_back(value);
688
                        // }
689

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

694
                        IterativeDropHandler::bfs(&mut drop_buffer);
695
                    }
696
                })
697
                .is_err()
698
            {
699
                let mut buffer = self.fields.drain(..).collect();
×
700

701
                IterativeDropHandler::bfs(&mut buffer);
×
702
            }
703
        }
704
    }
705

706
    impl Drop for LazyStream {
707
        fn drop(&mut self) {
116✔
708
            if self.initial_value == SteelVal::Void && self.stream_thunk == SteelVal::Void {
118✔
709
                return;
2✔
710
            }
711

712
            DROP_BUFFER
114✔
713
                .try_with(|drop_buffer| {
228✔
714
                    if let Ok(mut drop_buffer) = drop_buffer.try_borrow_mut() {
228✔
715
                        drop_buffer.push_back(self.initial_value.make_void());
716
                        drop_buffer.push_back(self.stream_thunk.make_void());
717

718
                        IterativeDropHandler::bfs(&mut drop_buffer);
719
                    }
720
                })
721
                .ok();
722
        }
723
    }
724

725
    // impl Drop for ByteCodeLambda {
726
    //     fn drop(&mut self) {
727
    //         if self.captures.is_empty() {
728
    //             return;
729
    //         }
730

731
    //         DROP_BUFFER
732
    //             .try_with(|drop_buffer| {
733
    //                 if let Ok(mut drop_buffer) = drop_buffer.try_borrow_mut() {
734
    //                     for value in std::mem::take(&mut self.captures) {
735
    //                         drop_buffer.push_back(value);
736
    //                     }
737

738
    //                     IterativeDropHandler::bfs(&mut drop_buffer);
739
    //                 }
740
    //             })
741
    //             .ok();
742
    //     }
743
    // }
744
}
745

746
pub struct IterativeDropHandler<'a> {
747
    drop_buffer: &'a mut VecDeque<SteelVal>,
748
}
749

750
impl<'a> IterativeDropHandler<'a> {
751
    pub fn bfs(drop_buffer: &'a mut VecDeque<SteelVal>) {
268,045✔
752
        // println!("Current depth: {}", DEPTH.with(|x| x.get()));
753

754
        // DEPTH.with(|x| x.set(x.get() + 1));
755
        IterativeDropHandler { drop_buffer }.visit();
756
        // DEPTH.with(|x| x.set(x.get() - 1));
757
    }
758
}
759

760
impl<'a> BreadthFirstSearchSteelValVisitor for IterativeDropHandler<'a> {
761
    type Output = ();
762

763
    fn default_output(&mut self) -> Self::Output {
268,045✔
764
        ()
765
    }
766

767
    fn pop_front(&mut self) -> Option<SteelVal> {
933,822✔
768
        self.drop_buffer.pop_front()
933,822✔
769
    }
770

771
    fn push_back(&mut self, value: SteelVal) {
597,499✔
772
        match &value {
597,499✔
773
            SteelVal::BoolV(_)
×
774
            | SteelVal::NumV(_)
×
775
            | SteelVal::IntV(_)
×
776
            | SteelVal::CharV(_)
×
777
            | SteelVal::Void
×
778
            | SteelVal::StringV(_)
×
779
            | SteelVal::FuncV(_)
×
780
            | SteelVal::SymbolV(_)
×
781
            | SteelVal::FutureFunc(_)
×
782
            | SteelVal::FutureV(_)
×
783
            | SteelVal::BoxedFunction(_)
×
784
            | SteelVal::MutFunc(_)
×
785
            | SteelVal::BuiltIn(_)
×
786
            | SteelVal::ByteVector(_)
×
787
            | SteelVal::BigNum(_) => return,
360,453✔
788
            _ => {
237,046✔
789
                self.drop_buffer.push_back(value);
237,046✔
790
            }
791
        }
792
    }
793

794
    fn visit_bytevector(&mut self, _bytevector: SteelByteVector) -> Self::Output {}
15✔
795
    fn visit_bool(&mut self, _boolean: bool) {}
3,847✔
796
    fn visit_float(&mut self, _float: f64) {}
×
797
    fn visit_int(&mut self, _int: isize) {}
1,084✔
798
    fn visit_rational(&mut self, _: Rational32) {}
×
799
    fn visit_bigrational(&mut self, _: Gc<BigRational>) {}
×
800
    fn visit_char(&mut self, _c: char) {}
42✔
801
    fn visit_void(&mut self) {}
2,587✔
802
    fn visit_string(&mut self, _string: SteelString) {}
129✔
803
    fn visit_function_pointer(&mut self, _ptr: FunctionSignature) {}
483✔
804
    fn visit_symbol(&mut self, _symbol: SteelString) {}
19,974✔
805
    fn visit_port(&mut self, _port: SteelPort) {}
11,065✔
806
    fn visit_future(&mut self, _future: Gc<FutureResult>) {}
×
807
    fn visit_mutable_function(&mut self, _function: MutFunctionSignature) {}
×
808
    fn visit_complex(&mut self, _: Gc<SteelComplex>) {}
×
809
    fn visit_bignum(&mut self, _bignum: Gc<BigInt>) {}
×
810
    fn visit_future_function(&mut self, _function: BoxedAsyncFunctionSignature) {}
×
811
    fn visit_builtin_function(&mut self, _function: BuiltInSignature) {}
×
812
    fn visit_boxed_function(&mut self, _function: Gc<BoxedDynFunction>) {}
1,455✔
813

814
    fn visit_closure(&mut self, closure: Gc<ByteCodeLambda>) {
21,116✔
815
        if let Ok(mut inner) = closure.try_unwrap() {
27,272✔
816
            for value in std::mem::take(&mut inner.captures) {
13,452✔
817
                self.push_back(value);
6,726✔
818
            }
819
        }
820
    }
821

822
    fn visit_immutable_vector(&mut self, mut vector: SteelVector) {
79✔
823
        if let Some(inner) = vector.0.get_mut() {
150✔
824
            for value in std::mem::take(inner) {
420✔
825
                self.push_back(value);
210✔
826
            }
827
        }
828
    }
829

830
    fn visit_custom_type(&mut self, custom_type: GcMut<Box<dyn CustomType>>) {
1,518✔
831
        if let Ok(inner) = custom_type.try_unwrap() {
1,518✔
832
            let mut inner = inner.consume();
×
833

834
            // let this decide if we're doing anything with this custom type
835
            inner.drop_mut(self);
×
836
        }
837
    }
838

839
    fn visit_hash_map(&mut self, mut hashmap: SteelHashMap) {
100,001✔
840
        if let Some(inner) = hashmap.0.get_mut() {
200,001✔
841
            for (key, value) in std::mem::take(inner) {
200,000✔
842
                self.push_back(key);
100,000✔
843
                self.push_back(value);
100,000✔
844
            }
845
        }
846
    }
847

848
    fn visit_hash_set(&mut self, mut hashset: SteelHashSet) {
×
849
        if let Some(inner) = hashset.0.get_mut() {
×
850
            for key in std::mem::take(inner) {
×
851
                self.push_back(key);
×
852
            }
853
        }
854
    }
855

856
    fn visit_steel_struct(&mut self, steel_struct: Gc<UserDefinedStruct>) {
10,201✔
857
        if let Ok(mut inner) = steel_struct.try_unwrap() {
14,650✔
858
            for value in inner.fields.drain(..) {
23,390✔
859
                self.push_back(value);
11,695✔
860
            }
861
        }
862
    }
863

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

887
    fn visit_reducer(&mut self, reducer: Gc<Reducer>) {
×
888
        if let Ok(inner) = reducer.try_unwrap() {
×
889
            match inner {
×
890
                Reducer::ForEach(f) => self.push_back(f),
×
891
                Reducer::Generic(rf) => {
×
892
                    self.push_back(rf.initial_value);
×
893
                    self.push_back(rf.function);
×
894
                }
895
                _ => {}
×
896
            }
897
        }
898
    }
899

900
    fn visit_stream(&mut self, stream: Gc<LazyStream>) {
14✔
901
        if let Ok(mut inner) = stream.try_unwrap() {
16✔
902
            self.push_back(replace_with_void(&mut inner.initial_value));
×
903
            self.push_back(replace_with_void(&mut inner.stream_thunk));
×
904
        }
905
    }
906

907
    // Walk the whole thing! This includes the stack and all the stack frames
908
    fn visit_continuation(&mut self, continuation: Continuation) {
65✔
909
        if let Ok(inner) = crate::gc::Shared::try_unwrap(continuation.inner).map(|x| x.consume()) {
222✔
910
            match inner {
×
911
                ContinuationMark::Closed(mut inner) => {
21✔
912
                    for value in std::mem::take(&mut inner.stack) {
272✔
913
                        self.push_back(value);
136✔
914
                    }
915

916
                    if let Some(inner) = inner.current_frame.function.get_mut() {
21✔
917
                        for value in std::mem::take(&mut inner.captures) {
×
918
                            self.push_back(value);
×
919
                        }
920
                    }
921

922
                    for mut frame in std::mem::take(&mut inner.stack_frames) {
112✔
923
                        if let Some(inner) = frame.function.get_mut() {
96✔
924
                            for value in std::mem::take(&mut inner.captures) {
×
925
                                self.push_back(value);
×
926
                            }
927
                        }
928
                    }
929
                }
930

931
                ContinuationMark::Open(mut inner) => {
25✔
932
                    for value in inner.current_stack_values {
100✔
933
                        self.push_back(value);
25✔
934
                    }
935

936
                    if let Some(inner) = inner.current_frame.function.get_mut() {
25✔
937
                        for value in std::mem::take(&mut inner.captures) {
×
938
                            self.push_back(value);
×
939
                        }
940
                    }
941
                }
942
            }
943
        }
944
    }
945

946
    fn visit_list(&mut self, list: List<SteelVal>) {
227,787✔
947
        // println!("VISITING LIST: {}", list.strong_count());
948
        // println!("list: {:?}", list);
949

950
        if list.strong_count() == 1 {
227,787✔
951
            for value in list.draining_iterator() {
797,063✔
952
                // println!(
953
                // "PUSHING BACK VALUE - queue size: {}",
954
                // self.drop_buffer.len()
955
                // );
956

957
                // println!("enqueueing: {}", value);
958

959
                self.push_back(value);
335,034✔
960
            }
961
        }
962

963
        // if list.strong_count() == 1 {
964
        //     for value in list {
965
        //         self.push_back(value);
966
        //     }
967
        // }
968
    }
969

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

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

981
    fn visit_syntax_object(&mut self, syntax_object: Gc<Syntax>) {
16,183✔
982
        if let Ok(inner) = syntax_object.try_unwrap() {
32,294✔
983
            if let Some(raw) = inner.raw {
32,180✔
984
                self.push_back(raw);
16,090✔
985
            }
986

987
            self.push_back(inner.syntax);
16,111✔
988
        }
989
    }
990

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

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

1003
    fn visit_heap_allocated(&mut self, _heap_ref: HeapRef<SteelVal>) -> Self::Output {}
201,208✔
1004

1005
    fn visit(&mut self) -> Self::Output {
268,045✔
1006
        let mut ret = self.default_output();
268,045✔
1007

1008
        while let Some(value) = self.pop_front() {
1,599,599✔
1009
            ret = match value {
×
1010
                Closure(c) => self.visit_closure(c),
21,116✔
1011
                BoolV(b) => self.visit_bool(b),
3,847✔
1012
                NumV(n) => self.visit_float(n),
×
1013
                IntV(i) => self.visit_int(i),
1,084✔
1014
                Rational(x) => self.visit_rational(x),
×
1015
                BigRational(x) => self.visit_bigrational(x),
×
1016
                BigNum(b) => self.visit_bignum(b),
×
1017
                Complex(x) => self.visit_complex(x),
×
1018
                CharV(c) => self.visit_char(c),
42✔
1019
                VectorV(v) => self.visit_immutable_vector(v),
79✔
1020
                Void => self.visit_void(),
2,587✔
1021
                StringV(s) => self.visit_string(s),
129✔
1022
                FuncV(f) => self.visit_function_pointer(f),
483✔
1023
                SymbolV(s) => self.visit_symbol(s),
19,974✔
1024
                SteelVal::Custom(c) => self.visit_custom_type(c),
1,518✔
1025
                HashMapV(h) => self.visit_hash_map(h),
100,001✔
1026
                HashSetV(s) => self.visit_hash_set(s),
×
1027
                CustomStruct(c) => self.visit_steel_struct(c),
10,201✔
1028
                PortV(p) => self.visit_port(p),
11,065✔
1029
                IterV(t) => self.visit_transducer(t),
×
1030
                ReducerV(r) => self.visit_reducer(r),
×
1031
                FutureFunc(f) => self.visit_future_function(f),
×
1032
                FutureV(f) => self.visit_future(f),
×
1033
                StreamV(s) => self.visit_stream(s),
14✔
1034
                BoxedFunction(b) => self.visit_boxed_function(b),
1,455✔
1035
                ContinuationFunction(c) => self.visit_continuation(c),
65✔
1036
                ListV(l) => self.visit_list(l),
227,787✔
1037
                MutFunc(m) => self.visit_mutable_function(m),
×
1038
                BuiltIn(b) => self.visit_builtin_function(b),
×
1039
                MutableVector(b) => self.visit_mutable_vector(b),
40,004✔
1040
                BoxedIterator(b) => self.visit_boxed_iterator(b),
×
1041
                SteelVal::SyntaxObject(s) => self.visit_syntax_object(s),
16,183✔
1042
                Boxed(b) => self.visit_boxed_value(b),
×
1043
                Reference(r) => self.visit_reference_value(r),
×
1044
                HeapAllocated(b) => self.visit_heap_allocated(b),
201,208✔
1045
                Pair(p) => self.visit_pair(p),
6,920✔
1046
                ByteVector(b) => self.visit_bytevector(b),
15✔
1047
            };
1048
        }
1049

1050
        // println!("--- finished draining drop queue ----");
1051

1052
        ret
268,045✔
1053
    }
1054

1055
    fn visit_pair(&mut self, pair: Gc<Pair>) -> Self::Output {
6,920✔
1056
        if let Ok(inner) = Gc::try_unwrap(pair) {
12,654✔
1057
            self.push_back(inner.car);
×
1058
            self.push_back(inner.cdr);
×
1059
        }
1060
    }
1061
}
1062

1063
pub trait BreadthFirstSearchSteelValVisitor {
1064
    type Output;
1065

1066
    fn default_output(&mut self) -> Self::Output;
1067

1068
    fn pop_front(&mut self) -> Option<SteelVal>;
1069

1070
    fn push_back(&mut self, value: SteelVal);
1071

1072
    fn visit(&mut self) -> Self::Output {
12,838✔
1073
        let mut ret = self.default_output();
12,838✔
1074

1075
        while let Some(value) = self.pop_front() {
47,984✔
1076
            ret = match value {
17,573✔
1077
                Closure(c) => self.visit_closure(c),
340✔
1078
                BoolV(b) => self.visit_bool(b),
113✔
1079
                NumV(n) => self.visit_float(n),
26✔
1080
                IntV(i) => self.visit_int(i),
856✔
1081
                Rational(x) => self.visit_rational(x),
4✔
1082
                BigRational(x) => self.visit_bigrational(x),
2✔
1083
                BigNum(b) => self.visit_bignum(b),
×
1084
                Complex(x) => self.visit_complex(x),
×
1085
                CharV(c) => self.visit_char(c),
2,765✔
1086
                VectorV(v) => self.visit_immutable_vector(v),
×
1087
                Void => self.visit_void(),
25✔
1088
                StringV(s) => self.visit_string(s),
9,112✔
1089
                FuncV(f) => self.visit_function_pointer(f),
4✔
1090
                SymbolV(s) => self.visit_symbol(s),
227✔
1091
                SteelVal::Custom(c) => self.visit_custom_type(c),
50✔
1092
                HashMapV(h) => self.visit_hash_map(h),
16✔
1093
                HashSetV(s) => self.visit_hash_set(s),
1✔
1094
                CustomStruct(c) => self.visit_steel_struct(c),
130✔
1095
                PortV(p) => self.visit_port(p),
3✔
1096
                IterV(t) => self.visit_transducer(t),
×
1097
                ReducerV(r) => self.visit_reducer(r),
×
1098
                FutureFunc(f) => self.visit_future_function(f),
×
1099
                FutureV(f) => self.visit_future(f),
×
1100
                StreamV(s) => self.visit_stream(s),
14✔
1101
                BoxedFunction(b) => self.visit_boxed_function(b),
×
1102
                ContinuationFunction(c) => self.visit_continuation(c),
×
1103
                ListV(l) => self.visit_list(l),
228✔
1104
                MutFunc(m) => self.visit_mutable_function(m),
×
1105
                BuiltIn(b) => self.visit_builtin_function(b),
×
1106
                MutableVector(b) => self.visit_mutable_vector(b),
24✔
1107
                BoxedIterator(b) => self.visit_boxed_iterator(b),
×
1108
                SteelVal::SyntaxObject(s) => self.visit_syntax_object(s),
6✔
1109
                Boxed(b) => self.visit_boxed_value(b),
×
1110
                Reference(r) => self.visit_reference_value(r),
×
1111
                HeapAllocated(b) => self.visit_heap_allocated(b),
3,618✔
1112
                Pair(p) => self.visit_pair(p),
9✔
1113
                ByteVector(b) => self.visit_bytevector(b),
×
1114
            };
1115
        }
1116

1117
        ret
12,838✔
1118
    }
1119

1120
    fn visit_closure(&mut self, closure: Gc<ByteCodeLambda>) -> Self::Output;
1121
    fn visit_bool(&mut self, _: bool) -> Self::Output;
1122
    fn visit_float(&mut self, _: f64) -> Self::Output;
1123
    fn visit_int(&mut self, _: isize) -> Self::Output;
1124
    fn visit_rational(&mut self, _: Rational32) -> Self::Output;
1125
    fn visit_bigrational(&mut self, _: Gc<BigRational>) -> Self::Output;
1126
    fn visit_bignum(&mut self, _: Gc<BigInt>) -> Self::Output;
1127
    fn visit_complex(&mut self, _: Gc<SteelComplex>) -> Self::Output;
1128
    fn visit_char(&mut self, _: char) -> Self::Output;
1129
    fn visit_immutable_vector(&mut self, vector: SteelVector) -> Self::Output;
1130
    fn visit_void(&mut self) -> Self::Output;
1131
    fn visit_string(&mut self, string: SteelString) -> Self::Output;
1132
    fn visit_function_pointer(&mut self, ptr: FunctionSignature) -> Self::Output;
1133
    fn visit_symbol(&mut self, symbol: SteelString) -> Self::Output;
1134
    fn visit_custom_type(&mut self, custom_type: GcMut<Box<dyn CustomType>>) -> Self::Output;
1135
    fn visit_hash_map(&mut self, hashmap: SteelHashMap) -> Self::Output;
1136
    fn visit_hash_set(&mut self, hashset: SteelHashSet) -> Self::Output;
1137
    fn visit_steel_struct(&mut self, steel_struct: Gc<UserDefinedStruct>) -> Self::Output;
1138
    fn visit_port(&mut self, port: SteelPort) -> Self::Output;
1139
    fn visit_transducer(&mut self, transducer: Gc<Transducer>) -> Self::Output;
1140
    fn visit_reducer(&mut self, reducer: Gc<Reducer>) -> Self::Output;
1141
    fn visit_future_function(&mut self, function: BoxedAsyncFunctionSignature) -> Self::Output;
1142
    fn visit_future(&mut self, future: Gc<FutureResult>) -> Self::Output;
1143
    fn visit_stream(&mut self, stream: Gc<LazyStream>) -> Self::Output;
1144
    fn visit_boxed_function(&mut self, function: Gc<BoxedDynFunction>) -> Self::Output;
1145
    fn visit_continuation(&mut self, continuation: Continuation) -> Self::Output;
1146
    fn visit_list(&mut self, list: List<SteelVal>) -> Self::Output;
1147
    fn visit_mutable_function(&mut self, function: MutFunctionSignature) -> Self::Output;
1148
    fn visit_mutable_vector(&mut self, vector: HeapRef<Vec<SteelVal>>) -> Self::Output;
1149
    fn visit_builtin_function(&mut self, function: BuiltInSignature) -> Self::Output;
1150
    fn visit_boxed_iterator(&mut self, iterator: GcMut<OpaqueIterator>) -> Self::Output;
1151
    fn visit_syntax_object(&mut self, syntax_object: Gc<Syntax>) -> Self::Output;
1152
    fn visit_boxed_value(&mut self, boxed_value: GcMut<SteelVal>) -> Self::Output;
1153
    fn visit_reference_value(&mut self, reference: Gc<OpaqueReference<'static>>) -> Self::Output;
1154
    fn visit_heap_allocated(&mut self, heap_ref: HeapRef<SteelVal>) -> Self::Output;
1155
    fn visit_pair(&mut self, pair: Gc<Pair>) -> Self::Output;
1156
    fn visit_bytevector(&mut self, bytevector: SteelByteVector) -> Self::Output;
1157
}
1158

1159
pub trait BreadthFirstSearchSteelValReferenceVisitor<'a> {
1160
    type Output;
1161

1162
    fn default_output(&mut self) -> Self::Output;
1163

1164
    fn pop_front(&mut self) -> Option<&'a SteelVal>;
1165

1166
    fn push_back(&mut self, value: &'a SteelVal);
1167

1168
    fn visit(&mut self) -> Self::Output {
×
1169
        let mut ret = self.default_output();
×
1170

1171
        while let Some(value) = self.pop_front() {
×
1172
            ret = match value {
×
1173
                Closure(c) => self.visit_closure(c),
×
1174
                BoolV(b) => self.visit_bool(*b),
×
1175
                NumV(n) => self.visit_float(*n),
×
1176
                IntV(i) => self.visit_int(*i),
×
1177
                Rational(x) => self.visit_rational(*x),
×
1178
                BigRational(x) => self.visit_bigrational(x),
×
1179
                Complex(_) => unimplemented!(),
×
1180
                CharV(c) => self.visit_char(*c),
×
1181
                VectorV(v) => self.visit_immutable_vector(v),
×
1182
                Void => self.visit_void(),
×
1183
                StringV(s) => self.visit_string(s),
×
1184
                FuncV(f) => self.visit_function_pointer(*f),
×
1185
                SymbolV(s) => self.visit_symbol(s),
×
1186
                SteelVal::Custom(c) => self.visit_custom_type(c),
×
1187
                HashMapV(h) => self.visit_hash_map(h),
×
1188
                HashSetV(s) => self.visit_hash_set(s),
×
1189
                CustomStruct(c) => self.visit_steel_struct(c),
×
1190
                PortV(p) => self.visit_port(p),
×
1191
                IterV(t) => self.visit_transducer(t),
×
1192
                ReducerV(r) => self.visit_reducer(r),
×
1193
                FutureFunc(f) => self.visit_future_function(f),
×
1194
                FutureV(f) => self.visit_future(f),
×
1195
                StreamV(s) => self.visit_stream(s),
×
1196
                BoxedFunction(b) => self.visit_boxed_function(b),
×
1197
                ContinuationFunction(c) => self.visit_continuation(c),
×
1198
                ListV(l) => self.visit_list(l),
×
1199
                MutFunc(m) => self.visit_mutable_function(m),
×
1200
                BuiltIn(b) => self.visit_builtin_function(b),
×
1201
                MutableVector(b) => self.visit_mutable_vector(b),
×
1202
                BoxedIterator(b) => self.visit_boxed_iterator(b),
×
1203
                SteelVal::SyntaxObject(s) => self.visit_syntax_object(s),
×
1204
                Boxed(b) => self.visit_boxed_value(b),
×
1205
                Reference(r) => self.visit_reference_value(r),
×
1206
                BigNum(b) => self.visit_bignum(b),
×
1207
                HeapAllocated(b) => self.visit_heap_allocated(b),
×
1208
                Pair(p) => self.visit_pair(p),
×
1209
                ByteVector(b) => self.visit_bytevector(b),
×
1210
            };
1211
        }
1212

1213
        ret
×
1214
    }
1215

1216
    fn visit_bytevector(&mut self, _: &'a SteelByteVector) -> Self::Output;
1217
    fn visit_closure(&mut self, _: &'a Gc<ByteCodeLambda>) -> Self::Output;
1218
    fn visit_bool(&mut self, _: bool) -> Self::Output;
1219
    fn visit_float(&mut self, _: f64) -> Self::Output;
1220
    fn visit_int(&mut self, _: isize) -> Self::Output;
1221
    fn visit_rational(&mut self, _: Rational32) -> Self::Output;
1222
    fn visit_bigrational(&mut self, _: &'a Gc<BigRational>) -> Self::Output;
1223
    fn visit_bignum(&mut self, _: &'a Gc<BigInt>) -> Self::Output;
1224
    fn visit_char(&mut self, _: char) -> Self::Output;
1225
    fn visit_immutable_vector(&mut self, vector: &'a SteelVector) -> Self::Output;
1226
    fn visit_void(&mut self) -> Self::Output;
1227
    fn visit_string(&mut self, string: &'a SteelString) -> Self::Output;
1228
    fn visit_function_pointer(&mut self, ptr: FunctionSignature) -> Self::Output;
1229
    fn visit_symbol(&mut self, symbol: &'a SteelString) -> Self::Output;
1230
    fn visit_custom_type(&mut self, custom_type: &'a GcMut<Box<dyn CustomType>>) -> Self::Output;
1231
    fn visit_hash_map(&mut self, hashmap: &'a SteelHashMap) -> Self::Output;
1232
    fn visit_hash_set(&mut self, hashset: &'a SteelHashSet) -> Self::Output;
1233
    fn visit_steel_struct(&mut self, steel_struct: &'a Gc<UserDefinedStruct>) -> Self::Output;
1234
    fn visit_port(&mut self, port: &'a SteelPort) -> Self::Output;
1235
    fn visit_transducer(&mut self, transducer: &'a Gc<Transducer>) -> Self::Output;
1236
    fn visit_reducer(&mut self, reducer: &'a Gc<Reducer>) -> Self::Output;
1237
    fn visit_future_function(&mut self, function: &'a BoxedAsyncFunctionSignature) -> Self::Output;
1238
    fn visit_future(&mut self, future: &'a Gc<FutureResult>) -> Self::Output;
1239
    fn visit_stream(&mut self, stream: &'a Gc<LazyStream>) -> Self::Output;
1240
    fn visit_boxed_function(&mut self, function: &'a Gc<BoxedDynFunction>) -> Self::Output;
1241
    fn visit_continuation(&mut self, continuation: &'a Continuation) -> Self::Output;
1242
    fn visit_list(&mut self, list: &'a List<SteelVal>) -> Self::Output;
1243
    fn visit_mutable_function(&mut self, function: &'a MutFunctionSignature) -> Self::Output;
1244
    fn visit_mutable_vector(&mut self, vector: &'a HeapRef<Vec<SteelVal>>) -> Self::Output;
1245
    fn visit_builtin_function(&mut self, function: &'a BuiltInSignature) -> Self::Output;
1246
    fn visit_boxed_iterator(&mut self, iterator: &'a GcMut<OpaqueIterator>) -> Self::Output;
1247
    fn visit_syntax_object(&mut self, syntax_object: &'a Gc<Syntax>) -> Self::Output;
1248
    fn visit_boxed_value(&mut self, boxed_value: &'a GcMut<SteelVal>) -> Self::Output;
1249
    fn visit_reference_value(
1250
        &mut self,
1251
        reference: &'a Gc<OpaqueReference<'static>>,
1252
    ) -> Self::Output;
1253
    fn visit_heap_allocated(&mut self, heap_ref: &'a HeapRef<SteelVal>) -> Self::Output;
1254
    fn visit_pair(&mut self, pair: &'a Gc<Pair>) -> Self::Output;
1255
}
1256

1257
thread_local! {
1258
    static LEFT_QUEUE: RefCell<Vec<SteelVal>> = RefCell::new(Vec::with_capacity(128));
1259
    static RIGHT_QUEUE: RefCell<Vec<SteelVal>> = RefCell::new(Vec::with_capacity(128));
1260
    static VISITED_SET: RefCell<fxhash::FxHashSet<usize>> = RefCell::new(fxhash::FxHashSet::default());
1261
    static EQ_DEPTH: Cell<usize> = Cell::new(0);
1262
}
1263

1264
fn increment_eq_depth() {
×
1265
    #[cfg(feature = "sandbox")]
1266
    EQ_DEPTH.with(|x| x.set(x.get() + 1));
×
1267
}
1268

1269
fn decrement_eq_depth() {
×
1270
    #[cfg(feature = "sandbox")]
1271
    EQ_DEPTH.with(|x| x.set(x.get() - 1));
×
1272
}
1273

1274
fn reset_eq_depth() {
54,475✔
1275
    #[cfg(feature = "sandbox")]
1276
    EQ_DEPTH.with(|x| x.set(0));
54,475✔
1277
}
1278

1279
fn eq_depth() -> usize {
39✔
1280
    #[cfg(feature = "sandbox")]
1281
    return EQ_DEPTH.with(|x| x.get());
39✔
1282

1283
    #[cfg(not(feature = "sandbox"))]
39✔
1284
    0
39✔
1285
}
1286

1287
struct RecursiveEqualityHandler<'a> {
1288
    left: EqualityVisitor<'a>,
1289
    right: EqualityVisitor<'a>,
1290
    visited: &'a mut fxhash::FxHashSet<usize>,
1291
    // found_mutable_object: bool,
1292
}
1293

1294
impl<'a> RecursiveEqualityHandler<'a> {
1295
    pub fn compare_equality(&mut self, left: SteelVal, right: SteelVal) -> bool {
54,475✔
1296
        self.left.push_back(left);
54,475✔
1297
        self.right.push_back(right);
54,475✔
1298

1299
        self.visit()
54,475✔
1300
    }
1301

1302
    fn should_visit(&mut self, value: usize) -> bool {
80,880✔
1303
        // if !self.found_mutable_object {
1304
        // return true;
1305
        // }
1306

1307
        if self.visited.insert(value) {
80,880✔
1308
            return true;
80,879✔
1309
        }
1310

1311
        return false;
1✔
1312
    }
1313

1314
    fn visit(&mut self) -> bool {
54,475✔
1315
        loop {
×
1316
            let (left, right) = match (self.left.pop_front(), self.right.pop_front()) {
362,302✔
1317
                (Some(l), Some(r)) => (l, r),
160,128✔
1318
                (None, None) => return true,
42,046✔
1319
                _ => return false,
×
1320
            };
1321

1322
            // println!("{} - {}", left, right);
1323

1324
            // println!(
1325
            //     "Queue size: {:?}",
1326
            //     self.left.queue.len(),
1327
            //     // self.right.queue.len()
1328
            // );
1329

1330
            match (left, right) {
160,128✔
1331
                (ListV(l), ListV(r)) => {
40,522✔
1332
                    // If we've reached the same object, we're good
1333
                    if l.ptr_eq(&r) || l.storage_ptr_eq(&r) {
80,876✔
1334
                        continue;
168✔
1335
                    }
1336

1337
                    if self.should_visit(l.elements_as_ptr_usize())
40,354✔
1338
                        && self.should_visit(r.elements_as_ptr_usize())
40,354✔
1339
                    {
1340
                        if l.len() != r.len() {
40,353✔
1341
                            return false;
105✔
1342
                        }
1343

1344
                        for (lvalue, rvalue) in l.iter().zip(r.iter()) {
25,714✔
1345
                            // TODO: @Matt - need to do optimistic checks here so we don't
1346
                            // visit things we don't need to - basically a "check left" function
1347
                            match (lvalue, rvalue) {
25,714✔
1348
                                (SteelVal::ListV(llist), SteelVal::ListV(rlist))
328✔
1349
                                    if (llist.is_empty() && rlist.is_empty())
350✔
1350
                                        || llist.ptr_eq(&rlist)
306✔
1351
                                        || llist.storage_ptr_eq(&rlist) =>
328✔
1352
                                {
1353
                                    continue;
171✔
1354
                                }
1355
                                // (SteelVal::ListV(llist), SteelVal::ListV(rlist)) if llist.len() == 1 && rlist.len() == 1 {
1356

1357
                                // }
1358
                                (a, b) => {
25,543✔
1359
                                    // println!("Pushing back: {}", a);
1360

1361
                                    self.left.push_back(a.clone());
25,543✔
1362
                                    self.right.push_back(b.clone());
25,543✔
1363
                                }
1364
                            }
1365
                        }
1366
                    }
1367

1368
                    continue;
40,249✔
1369
                }
1370
                // If we run into issues with any stack overflow
1371
                // check here first.
1372
                (Pair(l), Pair(r)) => {
20✔
1373
                    if Gc::ptr_eq(&l, &r) {
20✔
1374
                        continue;
1✔
1375
                    }
1376

1377
                    self.left.push_back(l.car());
19✔
1378
                    self.right.push_back(r.car());
19✔
1379

1380
                    self.left.push_back(l.cdr());
19✔
1381
                    self.right.push_back(r.cdr());
19✔
1382
                }
1383
                (BoolV(l), BoolV(r)) => {
1✔
1384
                    if l != r {
1✔
1385
                        return false;
×
1386
                    }
1387

1388
                    continue;
1✔
1389
                }
1390
                (NumV(l), NumV(r)) => {
×
1391
                    if l != r {
×
1392
                        return false;
×
1393
                    }
1394

1395
                    continue;
×
1396
                }
1397
                (IntV(l), IntV(r)) => {
82,549✔
1398
                    if l != r {
82,549✔
1399
                        return false;
2✔
1400
                    }
1401

1402
                    continue;
82,547✔
1403
                }
1404
                (CharV(l), CharV(r)) => {
267✔
1405
                    if l != r {
267✔
1406
                        return false;
246✔
1407
                    }
1408

1409
                    continue;
21✔
1410
                }
1411
                (VectorV(l), VectorV(r)) => {
38✔
1412
                    if l.len() != r.len() {
38✔
1413
                        return false;
×
1414
                    }
1415

1416
                    // If these point to the same object, break early
1417
                    if Gc::ptr_eq(&l.0, &r.0) {
38✔
1418
                        continue;
×
1419
                    }
1420

1421
                    // Should we visit these?
1422
                    if self.should_visit(l.0.as_ptr() as usize)
38✔
1423
                        && self.should_visit(r.0.as_ptr() as usize)
38✔
1424
                    {
1425
                        self.left.visit_immutable_vector(l);
38✔
1426
                        self.right.visit_immutable_vector(r);
38✔
1427
                    } else {
1428
                        return false;
×
1429
                    }
1430

1431
                    continue;
38✔
1432
                }
1433

1434
                (VectorV(l), MutableVector(r)) => {
48✔
1435
                    if l.len() != r.get().len() {
48✔
1436
                        return false;
×
1437
                    }
1438

1439
                    self.left.visit_immutable_vector(l);
48✔
1440
                    self.right.visit_mutable_vector(r);
48✔
1441

1442
                    continue;
48✔
1443
                }
1444
                (MutableVector(l), VectorV(r)) => {
×
1445
                    if l.get().len() != r.len() {
×
1446
                        return false;
×
1447
                    }
1448

1449
                    self.left.visit_mutable_vector(l);
×
1450
                    self.right.visit_immutable_vector(r);
×
1451

1452
                    continue;
×
1453
                }
1454
                (Void, Void) => {
×
1455
                    continue;
×
1456
                }
1457
                (StringV(l), StringV(r)) => {
173✔
1458
                    if l != r {
173✔
1459
                        return false;
×
1460
                    }
1461
                    continue;
173✔
1462
                }
1463
                (FuncV(l), FuncV(r)) => {
×
1464
                    if l as usize != r as usize {
×
1465
                        return false;
×
1466
                    }
1467
                    continue;
×
1468
                }
1469
                (SymbolV(l), SymbolV(r)) => {
2,404✔
1470
                    if l != r {
2,404✔
UNCOV
1471
                        return false;
×
1472
                    }
1473
                    continue;
2,404✔
1474
                }
1475
                (SteelVal::Custom(l), SteelVal::Custom(r)) => {
10✔
1476
                    if l.read().inner_type_id() != r.read().inner_type_id() {
10✔
1477
                        return false;
×
1478
                    }
1479

1480
                    if l.read().check_equality_hint(r.read().as_ref()) {
10✔
1481
                        // Go down to the next level
1482
                        self.left.visit_custom_type(l);
10✔
1483
                        self.right.visit_custom_type(r);
10✔
1484
                        continue;
10✔
1485
                    } else {
1486
                        return false;
×
1487
                    }
1488
                }
1489

1490
                (SteelVal::Custom(l), other) => {
×
1491
                    if l.read().check_equality_hint_general(&other) {
×
1492
                        self.left.visit_custom_type(l);
×
1493

1494
                        match other {
×
1495
                            Closure(x) => self.right.visit_closure(x),
×
1496
                            VectorV(v) => self.right.visit_immutable_vector(v),
×
1497
                            SteelVal::Custom(r) => self.right.visit_custom_type(r),
×
1498
                            HashMapV(r) => self.right.visit_hash_map(r),
×
1499
                            HashSetV(r) => self.right.visit_hash_set(r),
×
1500
                            CustomStruct(r) => self.right.visit_steel_struct(r),
×
1501
                            PortV(r) => self.right.visit_port(r),
×
1502
                            IterV(r) => self.right.visit_transducer(r),
×
1503
                            ReducerV(r) => self.right.visit_reducer(r),
×
1504
                            ContinuationFunction(r) => self.right.visit_continuation(r),
×
1505
                            ListV(r) => self.right.visit_list(r),
×
1506
                            SteelVal::Pair(r) => self.right.visit_pair(r),
×
1507
                            MutableVector(r) => self.right.visit_mutable_vector(r),
×
1508
                            BoxedIterator(r) => self.right.visit_boxed_iterator(r),
×
1509
                            SteelVal::SyntaxObject(r) => self.right.visit_syntax_object(r),
×
1510
                            Boxed(r) => self.right.visit_boxed_value(r),
×
1511
                            HeapAllocated(r) => self.right.visit_heap_allocated(r),
×
1512
                            _ => {}
×
1513
                        }
1514

1515
                        continue;
×
1516
                    } else {
1517
                        return false;
×
1518
                    }
1519
                }
1520

1521
                (other, SteelVal::Custom(r)) => {
1✔
1522
                    if r.read().check_equality_hint_general(&other) {
1✔
1523
                        match other {
×
1524
                            Closure(x) => self.left.visit_closure(x),
×
1525
                            VectorV(v) => self.left.visit_immutable_vector(v),
×
1526
                            SteelVal::Custom(r) => self.left.visit_custom_type(r),
×
1527
                            HashMapV(r) => self.left.visit_hash_map(r),
×
1528
                            HashSetV(r) => self.left.visit_hash_set(r),
×
1529
                            CustomStruct(r) => self.left.visit_steel_struct(r),
×
1530
                            PortV(r) => self.left.visit_port(r),
×
1531
                            IterV(r) => self.left.visit_transducer(r),
×
1532
                            ReducerV(r) => self.left.visit_reducer(r),
×
1533
                            ContinuationFunction(r) => self.left.visit_continuation(r),
×
1534
                            ListV(r) => self.left.visit_list(r),
×
1535
                            SteelVal::Pair(r) => self.left.visit_pair(r),
×
1536
                            MutableVector(r) => self.left.visit_mutable_vector(r),
×
1537
                            BoxedIterator(r) => self.left.visit_boxed_iterator(r),
×
1538
                            SteelVal::SyntaxObject(r) => self.left.visit_syntax_object(r),
×
1539
                            Boxed(r) => self.left.visit_boxed_value(r),
×
1540
                            HeapAllocated(r) => self.left.visit_heap_allocated(r),
×
1541
                            _ => {}
×
1542
                        }
1543

1544
                        self.right.visit_custom_type(r);
×
1545

1546
                        continue;
×
1547
                    } else {
1548
                        return false;
1✔
1549
                    }
1550
                }
1551

1552
                (SteelVal::HashMapV(l), SteelVal::HashMapV(r)) => {
35✔
1553
                    if Gc::ptr_eq(&l.0, &r.0) {
35✔
1554
                        continue;
×
1555
                    }
1556

1557
                    if self.should_visit(l.0.as_ptr() as usize)
35✔
1558
                        && self.should_visit(r.0.as_ptr() as usize)
35✔
1559
                    {
1560
                        if l.len() != r.len() {
35✔
1561
                            return false;
×
1562
                        }
1563

1564
                        // TODO: Implicitly here we are assuming that this key was even hashable
1565
                        // to begin with, since it ended up in the right spot, and didn't blow
1566
                        // the stack on a recursive structure.
1567
                        //
1568
                        // This still does not handle the pathological edge case of something like
1569
                        // (hash (hash (hash ...) value) value)
1570
                        //
1571
                        // In this case, we'll get a stack overflow, when trying to compare equality
1572
                        // with these maps if they're sufficiently deep.
1573
                        //
1574
                        // The issue is that if the two maps are equivalent, we need to check the
1575
                        // existence of each key in the left map with each key in the right map.
1576
                        // Doing so invokes an equality check, where we'll then invoke this logic
1577
                        // again. We could solve this by disallowing hashmaps as keys - then
1578
                        // we would not the same issue where putting a hashmap into the map
1579
                        // causes the equality checks to go off the rails.
1580

1581
                        if eq_depth() > 512 {
35✔
1582
                            log::error!("Aborting eq checks before the stack overflows");
×
1583

1584
                            return false;
×
1585
                        }
1586

1587
                        for (key, value) in l.0.iter() {
75✔
1588
                            if let Some(right_value) = r.0.get(key) {
150✔
1589
                                self.left.push_back(value.clone());
×
1590
                                self.right.push_back(right_value.clone());
×
1591
                            } else {
1592
                                // We know that these are not equal, because the
1593
                                // key in the left map does not exist in the right
1594
                                return false;
×
1595
                            }
1596
                        }
1597
                    }
1598

1599
                    continue;
35✔
1600
                }
1601
                (HashSetV(l), HashSetV(r)) => {
4✔
1602
                    if Gc::ptr_eq(&l.0, &r.0) {
4✔
1603
                        continue;
×
1604
                    }
1605

1606
                    if self.should_visit(l.0.as_ptr() as usize)
4✔
1607
                        && self.should_visit(r.0.as_ptr() as usize)
4✔
1608
                    {
1609
                        if l.len() != r.len() {
4✔
1610
                            return false;
×
1611
                        }
1612
                        if eq_depth() > 512 {
4✔
1613
                            log::error!("Aborting eq checks before the stack overflows");
×
1614

1615
                            return false;
×
1616
                        }
1617

1618
                        for key in l.0.iter() {
11✔
1619
                            if !l.0.contains(key) {
11✔
1620
                                return false;
×
1621
                            }
1622
                        }
1623
                    }
1624

1625
                    continue;
4✔
1626
                }
1627
                (CustomStruct(l), CustomStruct(r)) => {
71✔
1628
                    // If these are the same object, just continue
1629
                    if Gc::ptr_eq(&l, &r) {
71✔
1630
                        continue;
62✔
1631
                    }
1632

1633
                    if self.should_visit(l.as_ptr() as usize)
9✔
1634
                        && self.should_visit(r.as_ptr() as usize)
9✔
1635
                    {
1636
                        // Check the top level equality indicators to make sure
1637
                        // that these two types are the same
1638
                        if !(l.type_descriptor == r.type_descriptor && l.name() == r.name()) {
18✔
1639
                            return false;
×
1640
                        }
1641

1642
                        self.left.visit_steel_struct(l);
9✔
1643
                        self.right.visit_steel_struct(r);
9✔
1644
                    }
1645

1646
                    continue;
9✔
1647
                }
1648
                // (PortV(_), PortV(_)) => {
1649
                // return
1650
                // }
1651
                (IterV(l), IterV(r)) => {
×
1652
                    self.left.visit_transducer(l);
×
1653
                    self.right.visit_transducer(r);
×
1654

1655
                    continue;
×
1656
                }
1657
                (ReducerV(l), ReducerV(r)) => {
×
1658
                    self.left.visit_reducer(l);
×
1659
                    self.right.visit_reducer(r);
×
1660

1661
                    continue;
×
1662
                }
1663
                // FutureV(f) => self.visit_future(f),
1664
                (ContinuationFunction(l), ContinuationFunction(r)) => {
×
1665
                    if !Continuation::ptr_eq(&l, &r) {
×
1666
                        return false;
×
1667
                    }
1668

1669
                    continue;
×
1670
                }
1671
                // MutFunc(m) => self.visit_mutable_function(m),
1672
                (BuiltIn(l), BuiltIn(r)) => {
×
1673
                    if l as usize != r as usize {
×
1674
                        return false;
×
1675
                    }
1676
                    continue;
×
1677
                }
1678
                // MutableVector(b) => self.visit_mutable_vector(b),
1679
                // BoxedIterator(b) => self.visit_boxed_iterator(b),
1680
                // SteelVal::SyntaxObject(s) => self.visit_syntax_object(s),
1681
                // Boxed(b) => self.visit_boxed_value(b),
1682
                // Reference(r) => self.visit_reference_value(r),
1683
                (BigNum(l), BigNum(r)) => {
2✔
1684
                    if l != r {
2✔
1685
                        return false;
×
1686
                    }
1687
                    continue;
2✔
1688
                }
1689
                (SyntaxObject(l), SyntaxObject(r)) => {
×
1690
                    if Gc::ptr_eq(&l, &r) {
×
1691
                        continue;
×
1692
                    }
1693

1694
                    self.left.visit_syntax_object(l);
×
1695
                    self.right.visit_syntax_object(r);
×
1696

1697
                    continue;
×
1698
                }
1699
                (HeapAllocated(l), HeapAllocated(r)) => {
20✔
1700
                    if HeapRef::ptr_eq(&l, &r) {
20✔
1701
                        continue;
20✔
1702
                    }
1703

1704
                    self.left.visit_heap_allocated(l);
×
1705
                    self.right.visit_heap_allocated(r);
×
1706

1707
                    continue;
×
1708
                }
1709

1710
                (MutableVector(l), MutableVector(r)) => {
20,004✔
1711
                    if HeapRef::ptr_eq(&l, &r) {
20,004✔
1712
                        continue;
×
1713
                    }
1714

1715
                    self.left.visit_mutable_vector(l);
20,004✔
1716
                    self.right.visit_mutable_vector(r);
20,004✔
1717

1718
                    continue;
20,004✔
1719
                }
1720
                (Closure(l), Closure(r)) => {
2,052✔
1721
                    if l != r {
2,052✔
1722
                        return false;
168✔
1723
                    }
1724

1725
                    self.left.visit_closure(l);
1,884✔
1726
                    self.right.visit_closure(r);
1,884✔
1727

1728
                    continue;
1,884✔
1729
                }
1730
                (_, _) => {
×
1731
                    return false;
11,907✔
1732
                }
1733
            }
1734

1735
            // unreachable!();
1736
        }
1737
    }
1738
}
1739

1740
// TODO: This _needs_ to use references. Or otherwise we'll thrash stuff on drop
1741
pub struct EqualityVisitor<'a> {
1742
    // Mark each node that we've visited, if we encounter any mutable objects
1743
    // on the way, then we'll start using the visited set. But we'll optimistically
1744
    // assume that there are no mutable objects, and we won't start using this
1745
    // until we absolutely have to.
1746
    // found_mutable_object: bool,
1747
    queue: &'a mut Vec<SteelVal>,
1748
}
1749

1750
impl<'a> BreadthFirstSearchSteelValVisitor for EqualityVisitor<'a> {
1751
    type Output = ();
1752

1753
    fn default_output(&mut self) -> Self::Output {}
×
1754

1755
    fn pop_front(&mut self) -> Option<SteelVal> {
404,348✔
1756
        self.queue.pop()
404,348✔
1757
    }
1758

1759
    fn push_back(&mut self, value: SteelVal) {
320,932✔
1760
        self.queue.push(value)
320,932✔
1761
    }
1762

1763
    fn visit_closure(&mut self, _closure: Gc<ByteCodeLambda>) -> Self::Output {}
3,768✔
1764

1765
    // Leaf nodes, we don't need to do anything here
1766
    fn visit_bytevector(&mut self, _bytevector: SteelByteVector) -> Self::Output {}
×
1767
    fn visit_bool(&mut self, _boolean: bool) -> Self::Output {}
×
1768
    fn visit_float(&mut self, _float: f64) -> Self::Output {}
×
1769
    fn visit_int(&mut self, _int: isize) -> Self::Output {}
×
1770
    fn visit_rational(&mut self, _: Rational32) -> Self::Output {}
×
1771
    fn visit_bigrational(&mut self, _: Gc<BigRational>) -> Self::Output {}
×
1772
    fn visit_bignum(&mut self, _: Gc<BigInt>) -> Self::Output {}
×
1773
    fn visit_complex(&mut self, _: Gc<SteelComplex>) -> Self::Output {}
×
1774
    fn visit_char(&mut self, _c: char) -> Self::Output {}
×
1775
    fn visit_void(&mut self) -> Self::Output {}
×
1776
    fn visit_string(&mut self, _string: SteelString) -> Self::Output {}
×
1777
    fn visit_function_pointer(&mut self, _ptr: FunctionSignature) -> Self::Output {}
×
1778
    fn visit_symbol(&mut self, _symbol: SteelString) -> Self::Output {}
×
1779
    fn visit_port(&mut self, _port: SteelPort) -> Self::Output {}
×
1780
    fn visit_boxed_function(&mut self, _function: Gc<BoxedDynFunction>) -> Self::Output {}
×
1781
    fn visit_mutable_function(&mut self, _function: MutFunctionSignature) -> Self::Output {}
×
1782
    fn visit_builtin_function(&mut self, _function: BuiltInSignature) -> Self::Output {}
×
1783

1784
    //
1785
    fn visit_immutable_vector(&mut self, vector: SteelVector) -> Self::Output {
124✔
1786
        // If we've found the mutable object, mark that this has been visited. Only
1787
        // if self.should_visit(vector.0.as_ptr() as usize) {
1788
        for value in vector.iter() {
934✔
1789
            self.push_back(value.clone());
405✔
1790
        }
1791
        // }
1792
    }
1793

1794
    // SHOULD SET MUTABLE HERE
1795
    fn visit_custom_type(&mut self, custom_type: GcMut<Box<dyn CustomType>>) -> Self::Output {
20✔
1796
        custom_type.read().visit_children_for_equality(self);
20✔
1797
    }
1798

1799
    fn visit_hash_map(&mut self, _hashmap: SteelHashMap) -> Self::Output {
×
1800
        // TODO: See comment above
1801
    }
1802

1803
    fn visit_hash_set(&mut self, _hashset: SteelHashSet) -> Self::Output {
×
1804
        // TODO: See comment above
1805
    }
1806

1807
    fn visit_steel_struct(&mut self, steel_struct: Gc<UserDefinedStruct>) -> Self::Output {
18✔
1808
        // if self.should_visit(steel_struct.as_ptr() as usize) {
1809
        for value in steel_struct.fields.iter() {
62✔
1810
            self.push_back(value.clone());
22✔
1811
        }
1812
        // }
1813
    }
1814

1815
    fn visit_transducer(&mut self, transducer: Gc<Transducer>) -> Self::Output {
×
1816
        for transducer in transducer.ops.iter() {
×
1817
            match transducer.clone() {
×
1818
                crate::values::transducers::Transducers::Map(m) => self.push_back(m),
×
1819
                crate::values::transducers::Transducers::Filter(v) => self.push_back(v),
×
1820
                crate::values::transducers::Transducers::Take(t) => self.push_back(t),
×
1821
                crate::values::transducers::Transducers::Drop(d) => self.push_back(d),
×
1822
                crate::values::transducers::Transducers::FlatMap(fm) => self.push_back(fm),
×
1823
                crate::values::transducers::Transducers::Flatten => {}
×
1824
                crate::values::transducers::Transducers::Window(w) => self.push_back(w),
×
1825
                crate::values::transducers::Transducers::TakeWhile(tw) => self.push_back(tw),
×
1826
                crate::values::transducers::Transducers::DropWhile(dw) => self.push_back(dw),
×
1827
                crate::values::transducers::Transducers::Extend(e) => self.push_back(e),
×
1828
                crate::values::transducers::Transducers::Cycle => {}
×
1829
                crate::values::transducers::Transducers::Enumerating => {}
×
1830
                crate::values::transducers::Transducers::Zipping(z) => self.push_back(z),
×
1831
                crate::values::transducers::Transducers::Interleaving(i) => self.push_back(i),
×
1832
            }
1833
        }
1834
    }
1835

1836
    fn visit_reducer(&mut self, reducer: Gc<Reducer>) -> Self::Output {
×
1837
        match reducer.as_ref().clone() {
×
1838
            Reducer::ForEach(f) => self.push_back(f),
×
1839
            Reducer::Generic(rf) => {
×
1840
                self.push_back(rf.initial_value);
×
1841
                self.push_back(rf.function);
×
1842
            }
1843
            _ => {}
×
1844
        }
1845
    }
1846

1847
    fn visit_future_function(&mut self, _function: BoxedAsyncFunctionSignature) -> Self::Output {}
×
1848
    fn visit_future(&mut self, _future: Gc<FutureResult>) -> Self::Output {}
×
1849

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

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

1854
    fn visit_list(&mut self, list: List<SteelVal>) -> Self::Output {
×
1855
        for value in list.iter() {
×
1856
            self.push_back(value.clone());
×
1857
        }
1858
    }
1859

1860
    fn visit_mutable_vector(&mut self, vector: HeapRef<Vec<SteelVal>>) -> Self::Output {
40,056✔
1861
        for value in vector.get().iter() {
360,542✔
1862
            self.push_back(value.clone());
160,243✔
1863
        }
1864
    }
1865

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

1868
    fn visit_syntax_object(&mut self, syntax_object: Gc<Syntax>) -> Self::Output {
×
1869
        if let Some(raw) = syntax_object.raw.clone() {
×
1870
            self.push_back(raw);
×
1871
        }
1872

1873
        self.push_back(syntax_object.syntax.clone());
×
1874
    }
1875

1876
    fn visit_boxed_value(&mut self, boxed_value: GcMut<SteelVal>) -> Self::Output {
×
1877
        self.push_back(boxed_value.read().clone());
×
1878
    }
1879

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

1882
    // Should set mutable here
1883
    fn visit_heap_allocated(&mut self, heap_ref: HeapRef<SteelVal>) -> Self::Output {
×
1884
        // self.found_mutable_object = true;
1885

1886
        // if self.should_visit(heap_ref.as_ptr_usize()) {
1887
        self.push_back(heap_ref.get());
×
1888
        // }
1889
    }
1890

1891
    fn visit_pair(&mut self, pair: Gc<Pair>) -> Self::Output {
×
1892
        self.push_back(pair.car());
×
1893
        self.push_back(pair.cdr());
×
1894
    }
1895
}
1896

1897
impl PartialEq for SteelVal {
1898
    fn eq(&self, other: &Self) -> bool {
503,810✔
1899
        match (self, other) {
503,810✔
1900
            (Void, Void) => true,
447✔
1901
            (BoolV(l), BoolV(r)) => l == r,
16,807✔
1902
            (IntV(l), IntV(r)) => l == r,
246,984✔
1903
            (NumV(l), NumV(r)) => l == r,
259✔
1904
            (Rational(l), Rational(r)) => l == r,
88✔
1905
            (BigRational(l), BigRational(r)) => l == r,
5✔
1906
            (BigNum(l), BigNum(r)) => l == r,
54✔
1907
            (Complex(l), Complex(r)) => l == r,
57✔
1908
            (StringV(l), StringV(r)) => l == r,
10,907✔
1909
            (SymbolV(l), SymbolV(r)) => l == r,
135,574✔
1910
            (CharV(l), CharV(r)) => l == r,
38,128✔
1911
            (FuncV(l), FuncV(r)) => *l as usize == *r as usize,
6✔
1912
            (ByteVector(l), ByteVector(r)) => l == r,
19✔
1913
            // (VectorV(l), VectorV(r)) => l == r,
1914
            // (ListV(l), ListV(r)) => l == r,
1915
            // (HashSetV(l), HashSetV(r)) => l == r,
1916
            // (HashMapV(l), HashMapV(r)) => l == r,
1917
            // (Closure(l), Closure(r)) => l == r,
1918
            // (IterV(l), IterV(r)) => l == r,
1919
            // (ListV(l), ListV(r)) => l == r,
1920
            // (CustomStruct(l), CustomStruct(r)) => l == r,
1921
            // (Custom(l), Custom(r)) => Gc::ptr_eq(l, r),
1922
            // (HeapAllocated(l), HeapAllocated(r)) => l.get() == r.get(),
1923
            (left, right) => {
54,475✔
1924
                LEFT_QUEUE.with(|left_queue| {
108,950✔
1925
                    RIGHT_QUEUE.with(|right_queue| {
108,950✔
1926
                        VISITED_SET.with(|visited_set| {
108,950✔
1927
                            match (
54,475✔
1928
                                left_queue.try_borrow_mut(),
54,475✔
1929
                                right_queue.try_borrow_mut(),
54,475✔
1930
                                visited_set.try_borrow_mut(),
54,475✔
1931
                            ) {
1932
                                (Ok(mut left_queue), Ok(mut right_queue), Ok(mut visited_set)) => {
54,475✔
1933
                                    let mut equality_handler = RecursiveEqualityHandler {
54,475✔
1934
                                        left: EqualityVisitor {
54,475✔
1935
                                            queue: &mut left_queue,
54,475✔
1936
                                        },
1937
                                        right: EqualityVisitor {
54,475✔
1938
                                            queue: &mut right_queue,
54,475✔
1939
                                        },
1940
                                        visited: &mut visited_set,
54,475✔
1941
                                    };
1942

1943
                                    let res = equality_handler
54,475✔
1944
                                        .compare_equality(left.clone(), right.clone());
54,475✔
1945

1946
                                    reset_eq_depth();
54,475✔
1947

1948
                                    // Clean up!
1949
                                    equality_handler.left.queue.clear();
54,475✔
1950
                                    equality_handler.right.queue.clear();
54,475✔
1951
                                    equality_handler.visited.clear();
54,475✔
1952

1953
                                    res
54,475✔
1954
                                }
1955
                                _ => {
1956
                                    let mut left_queue = Vec::new();
×
1957
                                    let mut right_queue = Vec::new();
×
1958

1959
                                    let mut visited_set = fxhash::FxHashSet::default();
×
1960

1961
                                    increment_eq_depth();
×
1962

1963
                                    let mut equality_handler = RecursiveEqualityHandler {
×
1964
                                        left: EqualityVisitor {
×
1965
                                            queue: &mut left_queue,
×
1966
                                        },
1967
                                        right: EqualityVisitor {
×
1968
                                            queue: &mut right_queue,
×
1969
                                        },
1970
                                        visited: &mut visited_set,
×
1971
                                    };
1972

1973
                                    let res = equality_handler
×
1974
                                        .compare_equality(left.clone(), right.clone());
×
1975

1976
                                    decrement_eq_depth();
×
1977

1978
                                    res
×
1979
                                }
1980
                            }
1981
                        })
1982
                    })
1983
                })
1984
            }
1985
        }
1986
    }
1987
}
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