rune/core/object/
tagged.rs

1use super::{
2    super::{
3        cons::Cons,
4        error::{Type, TypeError},
5        gc::Block,
6    },
7    ByteFnPrototype, ByteString, CharTableInner, GcString, LispBigInt, LispBuffer,
8};
9use super::{
10    ByteFn, CharTable, HashTable, LispFloat, LispHashTable, LispString, LispVec, Record,
11    RecordBuilder, SubrFn, Symbol, SymbolCell,
12};
13use crate::{
14    arith::{MAX_FIXNUM, MIN_FIXNUM},
15    core::{
16        env::sym,
17        gc::{DropStackElem, GcMoveable, GcState, Trace, TracePtr},
18    },
19};
20use bumpalo::collections::Vec as GcVec;
21use num_bigint::BigInt;
22use private::{Tag, TaggedPtr};
23use rune_core::hashmap::HashSet;
24use rune_macros::enum_methods;
25use std::marker::PhantomData;
26use std::{fmt, ptr::NonNull};
27
28#[derive(Copy, Clone, Debug, PartialEq, Eq)]
29pub(crate) struct RawObj {
30    ptr: *const u8,
31}
32
33unsafe impl Send for RawObj {}
34
35impl Default for RawObj {
36    fn default() -> Self {
37        Self { ptr: NIL.ptr }
38    }
39}
40
41/// A `nil` object.
42///
43/// The build.rs file guarantees that that `nil` is the first symbol in
44/// `BUILTIN_SYMBOLS`, so we know it will always be 0.
45pub(crate) const NIL: Object<'static> = unsafe { std::mem::transmute(0u64) };
46
47/// A `t` object.
48///
49/// The build.rs file guarantees that that `t` is the second symbol in
50/// `BUILTIN_SYMBOLS`, so we can rely on its value being constant.
51pub(crate) const TRUE: Object<'static> =
52    // offset from 0 by size of SymbolCell and then shift 8 to account for
53    // tagging
54    unsafe { std::mem::transmute(size_of::<SymbolCell>() << 8) };
55
56/// This type has two meanings, it is both a value that is tagged as well as
57/// something that is managed by the GC. It is intended to be pointer sized, and
58/// have a lifetime tied to the context which manages garbage collections. A Gc
59/// can be reinterpreted as any type that shares the same tag.
60#[derive(Copy, Clone)]
61pub(crate) struct Gc<T> {
62    ptr: *const u8,
63    _data: PhantomData<T>,
64}
65
66// TODO need to find a better way to handle this
67unsafe impl<T> Send for Gc<T> {}
68
69impl<T> Gc<T> {
70    const fn new(ptr: *const u8) -> Self {
71        Self { ptr, _data: PhantomData }
72    }
73
74    unsafe fn from_ptr<U>(ptr: *const U, tag: Tag) -> Self {
75        assert_eq!(size_of::<*const U>(), size_of::<*const ()>());
76        // TODO: Adding this check can result in better code gen when tagging
77        // and untagging
78        // let top = x >> 55;
79        // if top != 0 && top != -1 {
80        //     unsafe { std::hint::unreachable_unchecked(); }
81        // }
82        let ptr = ptr.cast::<u8>().map_addr(|x| (x << 8) | tag as usize);
83        Self::new(ptr)
84    }
85
86    fn untag_ptr(self) -> (*const u8, Tag) {
87        let ptr = self.ptr.map_addr(|x| ((x as isize) >> 8) as usize);
88        let tag = self.get_tag();
89        (ptr, tag)
90    }
91
92    fn get_tag(self) -> Tag {
93        unsafe { std::mem::transmute(self.ptr.addr() as u8) }
94    }
95
96    pub(crate) fn into_raw(self) -> RawObj {
97        RawObj { ptr: self.ptr }
98    }
99
100    pub(in crate::core) fn into_ptr(self) -> *const u8 {
101        self.ptr
102    }
103
104    pub(crate) unsafe fn from_raw(raw: RawObj) -> Self {
105        Self::new(raw.ptr)
106    }
107
108    pub(crate) unsafe fn from_raw_ptr(raw: *mut u8) -> Self {
109        Self::new(raw)
110    }
111
112    pub(crate) fn ptr_eq<U>(self, other: Gc<U>) -> bool {
113        self.ptr == other.ptr
114    }
115
116    pub(crate) fn as_obj(&self) -> Object<'_> {
117        Gc::new(self.ptr)
118    }
119}
120
121#[expect(clippy::wrong_self_convention)]
122impl<'ob, T> Gc<T>
123where
124    T: 'ob,
125{
126    pub(crate) fn as_obj_copy(self) -> Object<'ob> {
127        Gc::new(self.ptr)
128    }
129}
130
131/// The [TaggedPtr] trait is local to this module (by design). This trait
132/// exports the one pubic method we want (untag) so it can be used in other
133/// modules.
134pub(crate) trait Untag<T> {
135    fn untag_erased(self) -> T;
136}
137
138impl<T: TaggedPtr> Untag<T> for Gc<T> {
139    fn untag_erased(self) -> T {
140        T::untag(self)
141    }
142}
143
144impl<T> Gc<T>
145where
146    Gc<T>: Untag<T>,
147{
148    /// A non-trait version of [Untag::untag_erased]. This is useful when we
149    /// don't want to import the trait all over the place. The only time we need
150    /// to import the trait is in generic code.
151    pub(crate) fn untag(self) -> T {
152        Self::untag_erased(self)
153    }
154}
155
156/// A wrapper trait to expose the `tag` method for GC managed references and
157/// immediate values. This is convenient when we don't have access to the
158/// `Context` but want to retag a value. Doesn't currently have a lot of use.
159pub(crate) trait TagType
160where
161    Self: Sized,
162{
163    type Out;
164    fn tag(self) -> Gc<Self::Out>;
165}
166
167impl<T: TaggedPtr> TagType for T {
168    type Out = Self;
169    fn tag(self) -> Gc<Self> {
170        self.tag()
171    }
172}
173
174unsafe fn cast_gc<U, V>(e: Gc<U>) -> Gc<V> {
175    Gc::new(e.ptr)
176}
177
178impl<'a, T: 'a + Copy> From<Gc<T>> for ObjectType<'a> {
179    fn from(x: Gc<T>) -> Self {
180        Gc::new(x.ptr).untag()
181    }
182}
183
184impl<'a, T: 'a + Copy> From<&Gc<T>> for ObjectType<'a> {
185    fn from(x: &Gc<T>) -> Self {
186        Gc::new(x.ptr).untag()
187    }
188}
189
190////////////////////////
191// Traits for Objects //
192////////////////////////
193
194/// Helper trait to change the lifetime of a Gc mangaged type. This is useful
195/// because objects are initially tied to the lifetime of the
196/// [Context](crate::core::gc::Context) they are allocated in. But when rooted
197/// the lifetime is dissociated from the Context. If we only worked with
198/// references, we could just use transmutes or casts to handle this, but
199/// generic types don't expose their lifetimes. This trait is used to work
200/// around that. Must be used with extreme care, as it is easy to cast it to an
201/// invalid lifetime.
202pub(crate) trait WithLifetime<'new> {
203    type Out: 'new;
204    unsafe fn with_lifetime(self) -> Self::Out;
205}
206
207impl<'new, T: WithLifetime<'new>> WithLifetime<'new> for Gc<T> {
208    type Out = Gc<<T as WithLifetime<'new>>::Out>;
209
210    unsafe fn with_lifetime(self) -> Self::Out {
211        cast_gc(self)
212    }
213}
214
215impl<'new, T, const N: usize> WithLifetime<'new> for [T; N]
216where
217    T: WithLifetime<'new>,
218{
219    type Out = [<T as WithLifetime<'new>>::Out; N];
220    unsafe fn with_lifetime(self) -> Self::Out {
221        // work around since we can't transmute arrays
222        let ptr = &self as *const [T; N] as *const Self::Out;
223        let value = unsafe { ptr.read() };
224        std::mem::forget(self);
225        value
226    }
227}
228
229impl<'new, T> WithLifetime<'new> for Vec<T>
230where
231    T: WithLifetime<'new>,
232{
233    type Out = Vec<<T as WithLifetime<'new>>::Out>;
234
235    unsafe fn with_lifetime(self) -> Self::Out {
236        std::mem::transmute(self)
237    }
238}
239
240impl<'new, T> WithLifetime<'new> for std::collections::VecDeque<T>
241where
242    T: WithLifetime<'new>,
243{
244    type Out = std::collections::VecDeque<<T as WithLifetime<'new>>::Out>;
245
246    unsafe fn with_lifetime(self) -> Self::Out {
247        std::mem::transmute(self)
248    }
249}
250
251impl<'new, T> WithLifetime<'new> for Option<T>
252where
253    T: WithLifetime<'new>,
254{
255    type Out = Option<<T as WithLifetime<'new>>::Out>;
256
257    unsafe fn with_lifetime(self) -> Self::Out {
258        self.map(|x| x.with_lifetime())
259    }
260}
261
262impl<'new, T, U> WithLifetime<'new> for (T, U)
263where
264    T: WithLifetime<'new>,
265    U: WithLifetime<'new>,
266{
267    type Out = (<T as WithLifetime<'new>>::Out, <U as WithLifetime<'new>>::Out);
268
269    unsafe fn with_lifetime(self) -> Self::Out {
270        (self.0.with_lifetime(), self.1.with_lifetime())
271    }
272}
273
274macro_rules! object_trait_impls {
275    ($ty:ty) => {
276        impl<'old, 'new> WithLifetime<'new> for &'old $ty {
277            type Out = &'new $ty;
278
279            unsafe fn with_lifetime(self) -> Self::Out {
280                std::mem::transmute(self)
281            }
282        }
283        impl GcPtr for &$ty {}
284    };
285}
286
287pub(in crate::core) trait GcPtr {}
288impl<T> GcPtr for Gc<T> {}
289impl GcPtr for Symbol<'_> {}
290
291object_trait_impls!(LispFloat);
292object_trait_impls!(Cons);
293object_trait_impls!(ByteFn);
294object_trait_impls!(LispString);
295object_trait_impls!(ByteString);
296object_trait_impls!(LispVec);
297object_trait_impls!(Record);
298object_trait_impls!(LispHashTable);
299object_trait_impls!(LispBuffer);
300object_trait_impls!(CharTable);
301object_trait_impls!(LispBigInt);
302
303/// Trait for types that can be managed by the GC. This trait is implemented for
304/// as many types as possible, even for types that are already Gc managed, Like
305/// `Gc<T>`. This makes it easier to write generic code for working with Gc types.
306pub(crate) trait IntoObject {
307    type Out<'ob>;
308
309    fn into_obj<const C: bool>(self, block: &Block<C>) -> Gc<Self::Out<'_>>;
310}
311
312impl<T> IntoObject for Gc<T> {
313    type Out<'ob> = ObjectType<'ob>;
314
315    fn into_obj<const C: bool>(self, _block: &Block<C>) -> Gc<Self::Out<'_>> {
316        unsafe { cast_gc(self) }
317    }
318}
319
320impl<T> IntoObject for Option<T>
321where
322    T: IntoObject,
323{
324    type Out<'ob> = ObjectType<'ob>;
325
326    fn into_obj<const C: bool>(self, block: &Block<C>) -> Gc<Self::Out<'_>> {
327        match self {
328            Some(x) => unsafe { cast_gc(x.into_obj(block)) },
329            None => NIL,
330        }
331    }
332}
333
334impl<T> IntoObject for T
335where
336    T: TagType,
337{
338    type Out<'ob> = <T as TagType>::Out;
339
340    fn into_obj<const C: bool>(self, _block: &Block<C>) -> Gc<Self::Out<'_>> {
341        self.tag()
342    }
343}
344
345impl IntoObject for f64 {
346    type Out<'ob> = &'ob LispFloat;
347
348    fn into_obj<const C: bool>(self, block: &Block<C>) -> Gc<Self::Out<'_>> {
349        let ptr = block.objects.alloc(LispFloat::new(self, C));
350        unsafe { Self::Out::tag_ptr(ptr) }
351    }
352}
353
354impl IntoObject for bool {
355    type Out<'a> = Symbol<'a>;
356
357    fn into_obj<const C: bool>(self, _: &Block<C>) -> Gc<Self::Out<'_>> {
358        let sym = match self {
359            true => sym::TRUE,
360            false => sym::NIL,
361        };
362        unsafe { Self::Out::tag_ptr(sym.get_ptr()) }
363    }
364}
365
366impl IntoObject for () {
367    type Out<'a> = Symbol<'a>;
368
369    fn into_obj<const C: bool>(self, _: &Block<C>) -> Gc<Self::Out<'_>> {
370        unsafe { Self::Out::tag_ptr(sym::NIL.get_ptr()) }
371    }
372}
373
374impl IntoObject for Cons {
375    type Out<'ob> = &'ob Cons;
376
377    fn into_obj<const C: bool>(self, block: &Block<C>) -> Gc<Self::Out<'_>> {
378        let ptr = block.objects.alloc(self);
379        if C {
380            ptr.mark_const();
381        }
382        unsafe { Self::Out::tag_ptr(ptr) }
383    }
384}
385
386impl IntoObject for ByteFnPrototype {
387    type Out<'ob> = &'ob ByteFn;
388
389    fn into_obj<const C: bool>(self, block: &Block<C>) -> Gc<Self::Out<'_>> {
390        let ptr = block.objects.alloc(ByteFn::new(self, C));
391        unsafe { Self::Out::tag_ptr(ptr) }
392    }
393}
394
395impl IntoObject for SymbolCell {
396    type Out<'ob> = Symbol<'ob>;
397
398    fn into_obj<const C: bool>(self, block: &Block<C>) -> Gc<Self::Out<'_>> {
399        let ptr = block.objects.alloc(self);
400        let sym = unsafe { Symbol::from_ptr(ptr) };
401        unsafe { Self::Out::tag_ptr(sym.get_ptr()) }
402    }
403}
404
405impl IntoObject for String {
406    type Out<'ob> = &'ob LispString;
407
408    fn into_obj<const C: bool>(self, block: &Block<C>) -> Gc<Self::Out<'_>> {
409        unsafe {
410            let mut this = self;
411            let ptr = this.as_mut_str();
412            let ptr = block.objects.alloc(LispString::new(ptr, C));
413            block.drop_stack.borrow_mut().push(DropStackElem::String(this));
414            Self::Out::tag_ptr(ptr)
415        }
416    }
417}
418
419impl IntoObject for GcString<'_> {
420    type Out<'ob> = <String as IntoObject>::Out<'ob>;
421
422    fn into_obj<const C: bool>(self, block: &Block<C>) -> Gc<Self::Out<'_>> {
423        unsafe {
424            let mut this = self;
425            let ptr = block.objects.alloc(LispString::new(this.as_mut_str(), C));
426            std::mem::forget(this);
427            Self::Out::tag_ptr(ptr)
428        }
429    }
430}
431
432impl IntoObject for &str {
433    type Out<'ob> = <String as IntoObject>::Out<'ob>;
434
435    fn into_obj<const C: bool>(self, block: &Block<C>) -> Gc<Self::Out<'_>> {
436        GcString::from_str_in(self, &block.objects).into_obj(block)
437    }
438}
439
440impl IntoObject for Vec<u8> {
441    type Out<'ob> = &'ob ByteString;
442
443    fn into_obj<const C: bool>(self, block: &Block<C>) -> Gc<Self::Out<'_>> {
444        let mut this = self;
445        let slice = this.as_mut_slice();
446        let ptr = block.objects.alloc(ByteString::new(slice, C));
447        block.drop_stack.borrow_mut().push(DropStackElem::ByteString(this));
448        unsafe { <&ByteString>::tag_ptr(ptr) }
449    }
450}
451
452impl IntoObject for Vec<Object<'_>> {
453    type Out<'ob> = &'ob LispVec;
454
455    fn into_obj<const C: bool>(mut self, block: &Block<C>) -> Gc<Self::Out<'_>> {
456        unsafe {
457            // having the reference implicity cast a ptr triggers UB
458            let ptr = self.as_mut_slice() as *mut [Object];
459            let ptr = block.objects.alloc(LispVec::new(ptr, C));
460            block.drop_stack.borrow_mut().push(DropStackElem::Vec(self.with_lifetime()));
461            <&LispVec>::tag_ptr(ptr)
462        }
463    }
464}
465
466impl IntoObject for GcVec<'_, Object<'_>> {
467    type Out<'ob> = &'ob LispVec;
468
469    fn into_obj<const C: bool>(self, block: &Block<C>) -> Gc<Self::Out<'_>> {
470        unsafe {
471            // having the reference implicity cast a ptr triggers UB
472            let ptr = self.into_bump_slice_mut() as *mut [Object];
473            let ptr = block.objects.alloc(LispVec::new(ptr, C));
474            <&LispVec>::tag_ptr(ptr)
475        }
476    }
477}
478
479impl IntoObject for &[Object<'_>] {
480    type Out<'ob> = &'ob LispVec;
481
482    fn into_obj<const C: bool>(self, block: &Block<C>) -> Gc<Self::Out<'_>> {
483        let mut vec = GcVec::with_capacity_in(self.len(), &block.objects);
484        vec.extend_from_slice(self);
485        vec.into_obj(block)
486    }
487}
488
489impl IntoObject for RecordBuilder<'_> {
490    type Out<'ob> = &'ob Record;
491
492    fn into_obj<const C: bool>(self, block: &Block<C>) -> Gc<Self::Out<'_>> {
493        unsafe {
494            // record is the same layout as lispvec, just a different newtype wrapper
495            let ptr = self.0.into_bump_slice_mut() as *mut [Object];
496            let ptr = block.objects.alloc(LispVec::new(ptr, C));
497            <&Record>::tag_ptr(ptr)
498        }
499    }
500}
501
502impl IntoObject for HashTable<'_> {
503    type Out<'ob> = &'ob LispHashTable;
504
505    fn into_obj<const C: bool>(self, block: &Block<C>) -> Gc<Self::Out<'_>> {
506        unsafe {
507            let ptr = block.objects.alloc(LispHashTable::new(self, C));
508            block.lisp_hashtables.borrow_mut().push(ptr);
509            <&LispHashTable>::tag_ptr(ptr)
510        }
511    }
512}
513
514impl IntoObject for CharTableInner<'_> {
515    type Out<'ob> = &'ob CharTable;
516
517    fn into_obj<const C: bool>(self, block: &Block<C>) -> Gc<Self::Out<'_>> {
518        unsafe {
519            let ptr = block.objects.alloc(CharTable::new(self, C));
520            <Self::Out<'_>>::tag_ptr(ptr)
521        }
522    }
523}
524
525impl IntoObject for BigInt {
526    type Out<'ob> = &'ob LispBigInt;
527
528    fn into_obj<const C: bool>(self, block: &Block<C>) -> super::Gc<Self::Out<'_>> {
529        unsafe {
530            let ptr = block.objects.alloc(LispBigInt::new(self, C));
531            // block.lisp_integers.borrow_mut().push(ptr);
532            <&LispBigInt>::tag_ptr(ptr)
533        }
534    }
535}
536
537mod private {
538    use super::{Gc, WithLifetime};
539
540    #[repr(u8)]
541    pub(crate) enum Tag {
542        // Symbol must be 0 to enable nil to be all zeroes
543        Symbol = 0,
544        Int,
545        Float,
546        Cons,
547        String,
548        ByteString,
549        Vec,
550        Record,
551        HashTable,
552        SubrFn,
553        ByteFn,
554        Buffer,
555        CharTable,
556        BigInt,
557    }
558
559    /// Trait for tagged pointers. Anything that can be stored and passed around
560    /// by the lisp machine should implement this trait. There are two "flavors"
561    /// of types that implement this trait. First is "base" types, which are
562    /// pointers to some memory managed by the GC with a unique tag (e.g
563    /// `LispString`, `LispVec`). This may seem stange that we would define a
564    /// tagged pointer when the type is known (i.e. `Gc<i64>`), but doing so let's
565    /// us reinterpret bits without changing the underlying memory. Base types
566    /// are untagged into a pointer.
567    ///
568    /// The second type of implementor is "sum" types which can represent more
569    /// then 1 base types (e.g `Object`, `List`). Sum types are untagged into an
570    /// enum. this let's us easily match against them to operate on the possible
571    /// value. Multiple sum types are defined (instead of just a single `Object`
572    /// type) to allow the rust code to be more precise in what values are
573    /// allowed.
574    ///
575    /// The tagging scheme uses the bottom byte of the `Gc` to represent the
576    /// tag, meaning that we have 256 possible values. The data is shifted left
577    /// by 8 bits, meaning that are fixnums are limited to 56 bits. This scheme
578    /// has the advantage that it is easy to get the tag (just read the byte)
579    /// and it maps nicely onto rusts enums. This method needs to be benchmarked
580    /// and could change in the future.
581    ///
582    /// Every method has a default implementation, and the doc string
583    /// indicates if it should be reimplemented or left untouched.
584    pub(super) trait TaggedPtr: Copy + for<'a> WithLifetime<'a> {
585        /// The type of object being pointed to. This will be different for all
586        /// implementors.
587        type Ptr;
588        /// Tag value. This is only applicable to base values. Use Int for sum
589        /// types.
590        const TAG: Tag;
591        /// Given a pointer to `Ptr` return a Tagged pointer.
592        ///
593        /// Base: default
594        /// Sum: implement
595        unsafe fn tag_ptr(ptr: *const Self::Ptr) -> Gc<Self> {
596            Gc::from_ptr(ptr, Self::TAG)
597        }
598
599        /// Remove the tag from the `Gc<T>` and return the inner type. If it is
600        /// base type then it will only have a single possible value and can be
601        /// untagged without checks, but sum types need to create all values
602        /// they can hold. We use tagged base types to let us reinterpret bits
603        /// without actually modify them.
604        ///
605        /// Base: default
606        /// Sum: implement
607        fn untag(val: Gc<Self>) -> Self {
608            let (ptr, _) = val.untag_ptr();
609            unsafe { Self::from_obj_ptr(ptr) }
610        }
611
612        /// Given the type, return a tagged version of it. When using a sum type
613        /// or an immediate value like i64, we override this method to set the
614        /// proper tag.
615        ///
616        /// Base: default
617        /// Sum: implement
618        fn tag(self) -> Gc<Self> {
619            unsafe { Self::tag_ptr(self.get_ptr()) }
620        }
621
622        /// Get the underlying pointer.
623        ///
624        /// Base: implement
625        /// Sum: default
626        fn get_ptr(self) -> *const Self::Ptr {
627            unimplemented!()
628        }
629
630        /// Given an untyped pointer, reinterpret to self.
631        ///
632        /// Base: implement
633        /// Sum: default
634        unsafe fn from_obj_ptr(_: *const u8) -> Self {
635            unimplemented!()
636        }
637    }
638}
639
640impl<'a> TaggedPtr for ObjectType<'a> {
641    type Ptr = ObjectType<'a>;
642    const TAG: Tag = Tag::Int;
643
644    unsafe fn tag_ptr(_: *const Self::Ptr) -> Gc<Self> {
645        unimplemented!()
646    }
647    fn untag(val: Gc<Self>) -> Self {
648        let (ptr, tag) = val.untag_ptr();
649        unsafe {
650            match tag {
651                Tag::Symbol => ObjectType::Symbol(<Symbol>::from_obj_ptr(ptr)),
652                Tag::Cons => ObjectType::Cons(<&Cons>::from_obj_ptr(ptr)),
653                Tag::SubrFn => ObjectType::SubrFn(&*ptr.cast()),
654                Tag::ByteFn => ObjectType::ByteFn(<&ByteFn>::from_obj_ptr(ptr)),
655                Tag::Int => ObjectType::Int(i64::from_obj_ptr(ptr)),
656                Tag::Float => ObjectType::Float(<&LispFloat>::from_obj_ptr(ptr)),
657                Tag::String => ObjectType::String(<&LispString>::from_obj_ptr(ptr)),
658                Tag::ByteString => ObjectType::ByteString(<&ByteString>::from_obj_ptr(ptr)),
659                Tag::Vec => ObjectType::Vec(<&LispVec>::from_obj_ptr(ptr)),
660                Tag::Record => ObjectType::Record(<&Record>::from_obj_ptr(ptr)),
661                Tag::HashTable => ObjectType::HashTable(<&LispHashTable>::from_obj_ptr(ptr)),
662                Tag::Buffer => ObjectType::Buffer(<&LispBuffer>::from_obj_ptr(ptr)),
663                Tag::CharTable => ObjectType::CharTable(<&CharTable>::from_obj_ptr(ptr)),
664                Tag::BigInt => ObjectType::BigInt(<&LispBigInt>::from_obj_ptr(ptr)),
665            }
666        }
667    }
668
669    fn tag(self) -> Gc<Self> {
670        match self {
671            ObjectType::Int(x) => TaggedPtr::tag(x).into(),
672            ObjectType::Float(x) => TaggedPtr::tag(x).into(),
673            ObjectType::Symbol(x) => TaggedPtr::tag(x).into(),
674            ObjectType::Cons(x) => TaggedPtr::tag(x).into(),
675            ObjectType::Vec(x) => TaggedPtr::tag(x).into(),
676            ObjectType::Record(x) => TaggedPtr::tag(x).into(),
677            ObjectType::HashTable(x) => TaggedPtr::tag(x).into(),
678            ObjectType::String(x) => TaggedPtr::tag(x).into(),
679            ObjectType::ByteString(x) => TaggedPtr::tag(x).into(),
680            ObjectType::ByteFn(x) => TaggedPtr::tag(x).into(),
681            ObjectType::SubrFn(x) => TaggedPtr::tag(x).into(),
682            ObjectType::Buffer(x) => TaggedPtr::tag(x).into(),
683            ObjectType::CharTable(x) => TaggedPtr::tag(x).into(),
684            ObjectType::BigInt(x) => TaggedPtr::tag(x).into(),
685        }
686    }
687}
688
689impl<'a> TaggedPtr for ListType<'a> {
690    type Ptr = ListType<'a>;
691    const TAG: Tag = Tag::Int;
692
693    unsafe fn tag_ptr(_: *const Self::Ptr) -> Gc<Self> {
694        unimplemented!()
695    }
696
697    fn untag(val: Gc<Self>) -> Self {
698        let (ptr, tag) = val.untag_ptr();
699        match tag {
700            Tag::Symbol => ListType::Nil,
701            Tag::Cons => ListType::Cons(unsafe { <&Cons>::from_obj_ptr(ptr) }),
702            _ => unreachable!(),
703        }
704    }
705
706    fn tag(self) -> Gc<Self> {
707        match self {
708            ListType::Nil => unsafe { cast_gc(TaggedPtr::tag(sym::NIL)) },
709            ListType::Cons(x) => TaggedPtr::tag(x).into(),
710        }
711    }
712}
713
714impl<'a> TaggedPtr for FunctionType<'a> {
715    type Ptr = FunctionType<'a>;
716    const TAG: Tag = Tag::Int;
717
718    unsafe fn tag_ptr(_: *const Self::Ptr) -> Gc<Self> {
719        unimplemented!()
720    }
721
722    fn untag(val: Gc<Self>) -> Self {
723        let (ptr, tag) = val.untag_ptr();
724        unsafe {
725            match tag {
726                Tag::Cons => FunctionType::Cons(<&Cons>::from_obj_ptr(ptr)),
727                // SubrFn does not have IntoObject implementation, so we cast it directly
728                Tag::SubrFn => FunctionType::SubrFn(&*ptr.cast::<SubrFn>()),
729                Tag::ByteFn => FunctionType::ByteFn(<&ByteFn>::from_obj_ptr(ptr)),
730                Tag::Symbol => FunctionType::Symbol(<Symbol>::from_obj_ptr(ptr)),
731                _ => unreachable!(),
732            }
733        }
734    }
735
736    fn tag(self) -> Gc<Self> {
737        match self {
738            FunctionType::Cons(x) => TaggedPtr::tag(x).into(),
739            FunctionType::SubrFn(x) => TaggedPtr::tag(x).into(),
740            FunctionType::ByteFn(x) => TaggedPtr::tag(x).into(),
741            FunctionType::Symbol(x) => TaggedPtr::tag(x).into(),
742        }
743    }
744}
745
746impl<'a> TaggedPtr for NumberType<'a> {
747    type Ptr = NumberType<'a>;
748    const TAG: Tag = Tag::Int;
749
750    unsafe fn tag_ptr(_: *const Self::Ptr) -> Gc<Self> {
751        unimplemented!()
752    }
753
754    fn untag(val: Gc<Self>) -> Self {
755        let (ptr, tag) = val.untag_ptr();
756        unsafe {
757            match tag {
758                Tag::Int => NumberType::Int(i64::from_obj_ptr(ptr)),
759                Tag::Float => NumberType::Float(<&LispFloat>::from_obj_ptr(ptr)),
760                Tag::BigInt => NumberType::Big(<&LispBigInt>::from_obj_ptr(ptr)),
761                _ => unreachable!(),
762            }
763        }
764    }
765
766    fn tag(self) -> Gc<Self> {
767        match self {
768            NumberType::Int(x) => TaggedPtr::tag(x).into(),
769            NumberType::Float(x) => TaggedPtr::tag(x).into(),
770            NumberType::Big(x) => TaggedPtr::tag(x).into(),
771        }
772    }
773}
774
775impl TaggedPtr for i64 {
776    type Ptr = i64;
777    const TAG: Tag = Tag::Int;
778
779    unsafe fn from_obj_ptr(ptr: *const u8) -> Self {
780        ptr.addr() as i64
781    }
782
783    fn get_ptr(self) -> *const Self::Ptr {
784        // prevent wrapping
785        let value = self.clamp(MIN_FIXNUM, MAX_FIXNUM);
786        core::ptr::without_provenance(value as usize)
787    }
788}
789
790pub(crate) fn int_to_char(int: i64) -> Result<char, TypeError> {
791    let err = TypeError::new(Type::Char, TagType::tag(int));
792    match u32::try_from(int) {
793        Ok(x) => match char::from_u32(x) {
794            Some(c) => Ok(c),
795            None => Err(err),
796        },
797        Err(_) => Err(err),
798    }
799}
800
801impl TaggedPtr for &LispFloat {
802    type Ptr = LispFloat;
803    const TAG: Tag = Tag::Float;
804    unsafe fn from_obj_ptr(ptr: *const u8) -> Self {
805        &*ptr.cast::<Self::Ptr>()
806    }
807
808    fn get_ptr(self) -> *const Self::Ptr {
809        self as *const Self::Ptr
810    }
811}
812
813impl TaggedPtr for &Cons {
814    type Ptr = Cons;
815    const TAG: Tag = Tag::Cons;
816    unsafe fn from_obj_ptr(ptr: *const u8) -> Self {
817        &*ptr.cast::<Self::Ptr>()
818    }
819
820    fn get_ptr(self) -> *const Self::Ptr {
821        self as *const Self::Ptr
822    }
823}
824
825impl TaggedPtr for &SubrFn {
826    type Ptr = SubrFn;
827    const TAG: Tag = Tag::SubrFn;
828    unsafe fn from_obj_ptr(ptr: *const u8) -> Self {
829        &*ptr.cast::<Self::Ptr>()
830    }
831
832    fn get_ptr(self) -> *const Self::Ptr {
833        self as *const Self::Ptr
834    }
835}
836
837impl TaggedPtr for Symbol<'_> {
838    type Ptr = u8;
839    const TAG: Tag = Tag::Symbol;
840
841    unsafe fn tag_ptr(ptr: *const Self::Ptr) -> Gc<Self> {
842        Gc::from_ptr(ptr, Self::TAG)
843    }
844
845    unsafe fn from_obj_ptr(ptr: *const u8) -> Self {
846        Symbol::from_offset_ptr(ptr)
847    }
848
849    fn get_ptr(self) -> *const Self::Ptr {
850        self.as_ptr()
851    }
852}
853
854impl TaggedPtr for &ByteFn {
855    type Ptr = ByteFn;
856    const TAG: Tag = Tag::ByteFn;
857    unsafe fn from_obj_ptr(ptr: *const u8) -> Self {
858        &*ptr.cast::<Self::Ptr>()
859    }
860
861    fn get_ptr(self) -> *const Self::Ptr {
862        self as *const Self::Ptr
863    }
864}
865
866impl TaggedPtr for &LispString {
867    type Ptr = LispString;
868    const TAG: Tag = Tag::String;
869    unsafe fn from_obj_ptr(ptr: *const u8) -> Self {
870        &*ptr.cast::<Self::Ptr>()
871    }
872
873    fn get_ptr(self) -> *const Self::Ptr {
874        self as *const Self::Ptr
875    }
876}
877
878impl TaggedPtr for &ByteString {
879    type Ptr = ByteString;
880    const TAG: Tag = Tag::ByteString;
881    unsafe fn from_obj_ptr(ptr: *const u8) -> Self {
882        &*ptr.cast::<Self::Ptr>()
883    }
884
885    fn get_ptr(self) -> *const Self::Ptr {
886        self as *const Self::Ptr
887    }
888}
889
890impl TaggedPtr for &LispVec {
891    type Ptr = LispVec;
892    const TAG: Tag = Tag::Vec;
893    unsafe fn from_obj_ptr(ptr: *const u8) -> Self {
894        &*ptr.cast::<Self::Ptr>()
895    }
896
897    fn get_ptr(self) -> *const Self::Ptr {
898        self as *const Self::Ptr
899    }
900}
901
902impl TaggedPtr for &Record {
903    type Ptr = LispVec;
904    const TAG: Tag = Tag::Record;
905    unsafe fn from_obj_ptr(ptr: *const u8) -> Self {
906        &*ptr.cast::<Record>()
907    }
908
909    fn get_ptr(self) -> *const Self::Ptr {
910        (self as *const Record).cast::<Self::Ptr>()
911    }
912}
913
914impl TaggedPtr for &LispHashTable {
915    type Ptr = LispHashTable;
916    const TAG: Tag = Tag::HashTable;
917    unsafe fn from_obj_ptr(ptr: *const u8) -> Self {
918        &*ptr.cast::<Self::Ptr>()
919    }
920
921    fn get_ptr(self) -> *const Self::Ptr {
922        self as *const Self::Ptr
923    }
924}
925
926impl TaggedPtr for &LispBuffer {
927    type Ptr = LispBuffer;
928    const TAG: Tag = Tag::Buffer;
929    unsafe fn from_obj_ptr(ptr: *const u8) -> Self {
930        &*ptr.cast::<Self::Ptr>()
931    }
932
933    fn get_ptr(self) -> *const Self::Ptr {
934        self as *const Self::Ptr
935    }
936}
937
938impl TaggedPtr for &CharTable {
939    type Ptr = CharTable;
940    const TAG: Tag = Tag::CharTable;
941
942    unsafe fn from_obj_ptr(ptr: *const u8) -> Self {
943        &*ptr.cast::<Self::Ptr>()
944    }
945
946    fn get_ptr(self) -> *const Self::Ptr {
947        self as *const Self::Ptr
948    }
949}
950
951impl TaggedPtr for &LispBigInt {
952    type Ptr = LispBigInt;
953    const TAG: Tag = Tag::BigInt;
954
955    unsafe fn from_obj_ptr(ptr: *const u8) -> Self {
956        &*ptr.cast::<Self::Ptr>()
957    }
958
959    fn get_ptr(self) -> *const Self::Ptr {
960        self as *const Self::Ptr
961    }
962}
963
964impl<T> TracePtr for Gc<T> {
965    fn trace_ptr(&self, state: &mut GcState) {
966        match self.as_obj().untag() {
967            ObjectType::Int(_) | ObjectType::SubrFn(_) => {}
968            ObjectType::Float(x) => x.trace(state),
969            ObjectType::String(x) => x.trace(state),
970            ObjectType::ByteString(x) => x.trace(state),
971            ObjectType::Vec(vec) => vec.trace(state),
972            ObjectType::Record(x) => x.trace(state),
973            ObjectType::HashTable(x) => x.trace(state),
974            ObjectType::Cons(x) => x.trace(state),
975            ObjectType::Symbol(x) => x.trace(state),
976            ObjectType::ByteFn(x) => x.trace(state),
977            ObjectType::Buffer(x) => x.trace(state),
978            ObjectType::CharTable(x) => x.trace(state),
979            ObjectType::BigInt(x) => x.trace(state),
980        }
981    }
982}
983
984macro_rules! cast_gc {
985    ($supertype:ty => $($subtype:ty),+ $(,)?) => {
986        $(
987            impl<'ob> From<Gc<$subtype>> for Gc<$supertype> {
988                fn from(x: Gc<$subtype>) -> Self {
989                    unsafe { cast_gc(x) }
990                }
991            }
992
993            impl<'ob> From<$subtype> for Gc<$supertype> {
994                fn from(x: $subtype) -> Self {
995                    unsafe { <$subtype>::tag_ptr(x.get_ptr()).into() }
996                }
997            }
998        )+
999    };
1000}
1001
1002////////////////////////
1003// Proc macro section //
1004////////////////////////
1005
1006// Number
1007#[derive(Copy, Clone)]
1008#[enum_methods(Number)]
1009#[repr(u8)]
1010/// The enum form of [Number] to take advantage of ergonomics of enums in Rust.
1011pub(crate) enum NumberType<'ob> {
1012    Int(i64) = Tag::Int as u8,
1013    Float(&'ob LispFloat) = Tag::Float as u8,
1014    Big(&'ob LispBigInt) = Tag::BigInt as u8,
1015}
1016cast_gc!(NumberType<'ob> => i64, &LispFloat, &LispBigInt);
1017
1018/// Represents a tagged pointer to a number value
1019pub(crate) type Number<'ob> = Gc<NumberType<'ob>>;
1020
1021impl<'old, 'new> WithLifetime<'new> for NumberType<'old> {
1022    type Out = NumberType<'new>;
1023
1024    unsafe fn with_lifetime(self) -> Self::Out {
1025        std::mem::transmute::<NumberType<'old>, NumberType<'new>>(self)
1026    }
1027}
1028
1029// List
1030#[derive(Copy, Clone)]
1031#[enum_methods(List)]
1032#[repr(u8)]
1033/// The enum form of [List] to take advantage of ergonomics of enums in Rust.
1034pub(crate) enum ListType<'ob> {
1035    // Since Tag::Symbol is 0 and sym::NIL is 0, we can use 0 as the nil value
1036    Nil = Tag::Symbol as u8,
1037    Cons(&'ob Cons) = Tag::Cons as u8,
1038}
1039cast_gc!(ListType<'ob> => &'ob Cons);
1040
1041/// Represents a tagged pointer to a list value (cons or nil)
1042pub(crate) type List<'ob> = Gc<ListType<'ob>>;
1043
1044impl ListType<'_> {
1045    pub(crate) fn empty() -> Gc<Self> {
1046        unsafe { cast_gc(NIL) }
1047    }
1048}
1049
1050impl<'old, 'new> WithLifetime<'new> for ListType<'old> {
1051    type Out = ListType<'new>;
1052
1053    unsafe fn with_lifetime(self) -> Self::Out {
1054        std::mem::transmute::<ListType<'old>, ListType<'new>>(self)
1055    }
1056}
1057
1058// Function
1059#[derive(Copy, Clone, Debug)]
1060#[enum_methods(Function)]
1061#[repr(u8)]
1062/// The enum form of [Function] to take advantage of ergonomics of enums in Rust.
1063pub(crate) enum FunctionType<'ob> {
1064    ByteFn(&'ob ByteFn) = Tag::ByteFn as u8,
1065    SubrFn(&'static SubrFn) = Tag::SubrFn as u8,
1066    Cons(&'ob Cons) = Tag::Cons as u8,
1067    Symbol(Symbol<'ob>) = Tag::Symbol as u8,
1068}
1069cast_gc!(FunctionType<'ob> => &'ob ByteFn, &'ob SubrFn, &'ob Cons, Symbol<'ob>);
1070
1071/// Represents a tagged pointer to a lisp object that could be interpreted as a
1072/// function. Note that not all `Function` types are valid functions (it could
1073/// be a cons cell for example).
1074pub(crate) type Function<'ob> = Gc<FunctionType<'ob>>;
1075
1076impl<'old, 'new> WithLifetime<'new> for FunctionType<'old> {
1077    type Out = FunctionType<'new>;
1078
1079    unsafe fn with_lifetime(self) -> Self::Out {
1080        std::mem::transmute::<FunctionType<'old>, FunctionType<'new>>(self)
1081    }
1082}
1083
1084#[derive(Copy, Clone, PartialEq, Eq)]
1085#[enum_methods(Object)]
1086#[repr(u8)]
1087/// The enum form of [Object] to take advantage of ergonomics of enums in Rust.
1088pub(crate) enum ObjectType<'ob> {
1089    Int(i64) = Tag::Int as u8,
1090    Float(&'ob LispFloat) = Tag::Float as u8,
1091    Symbol(Symbol<'ob>) = Tag::Symbol as u8,
1092    Cons(&'ob Cons) = Tag::Cons as u8,
1093    Vec(&'ob LispVec) = Tag::Vec as u8,
1094    Record(&'ob Record) = Tag::Record as u8,
1095    HashTable(&'ob LispHashTable) = Tag::HashTable as u8,
1096    String(&'ob LispString) = Tag::String as u8,
1097    ByteString(&'ob ByteString) = Tag::ByteString as u8,
1098    ByteFn(&'ob ByteFn) = Tag::ByteFn as u8,
1099    SubrFn(&'static SubrFn) = Tag::SubrFn as u8,
1100    Buffer(&'static LispBuffer) = Tag::Buffer as u8,
1101    CharTable(&'static CharTable) = Tag::CharTable as u8,
1102    BigInt(&'ob LispBigInt) = Tag::BigInt as u8,
1103}
1104
1105/// The Object defintion that contains all other possible lisp objects. This
1106/// type must remain covariant over 'ob.
1107pub(crate) type Object<'ob> = Gc<ObjectType<'ob>>;
1108
1109cast_gc!(ObjectType<'ob> => NumberType<'ob>,
1110         ListType<'ob>,
1111         FunctionType<'ob>,
1112         i64,
1113         Symbol<'_>,
1114         &'ob LispFloat,
1115         &'ob Cons,
1116         &'ob LispVec,
1117         &'ob Record,
1118         &'ob LispHashTable,
1119         &'ob LispString,
1120         &'ob ByteString,
1121         &'ob ByteFn,
1122         &'ob SubrFn,
1123         &'ob LispBuffer,
1124         &'ob CharTable,
1125         &'ob LispBigInt
1126);
1127
1128impl ObjectType<'_> {
1129    pub(crate) const NIL: ObjectType<'static> = ObjectType::Symbol(sym::NIL);
1130    pub(crate) const TRUE: ObjectType<'static> = ObjectType::Symbol(sym::TRUE);
1131    /// Return the type of an object
1132    pub(crate) fn get_type(self) -> Type {
1133        match self {
1134            ObjectType::Int(_) => Type::Int,
1135            ObjectType::Float(_) => Type::Float,
1136            ObjectType::Symbol(_) => Type::Symbol,
1137            ObjectType::Cons(_) => Type::Cons,
1138            ObjectType::Vec(_) => Type::Vec,
1139            ObjectType::Record(_) => Type::Record,
1140            ObjectType::HashTable(_) => Type::HashTable,
1141            ObjectType::String(_) => Type::String,
1142            ObjectType::ByteString(_) => Type::String,
1143            ObjectType::ByteFn(_) | ObjectType::SubrFn(_) => Type::Func,
1144            ObjectType::Buffer(_) => Type::Buffer,
1145            ObjectType::CharTable(_) => Type::CharTable,
1146            ObjectType::BigInt(_) => Type::BigInt,
1147        }
1148    }
1149}
1150
1151// Object Impl's
1152
1153impl<'old, 'new> WithLifetime<'new> for ObjectType<'old> {
1154    type Out = ObjectType<'new>;
1155
1156    unsafe fn with_lifetime(self) -> Self::Out {
1157        std::mem::transmute::<ObjectType<'old>, ObjectType<'new>>(self)
1158    }
1159}
1160
1161impl WithLifetime<'_> for i64 {
1162    type Out = i64;
1163
1164    unsafe fn with_lifetime(self) -> Self::Out {
1165        self
1166    }
1167}
1168
1169impl From<usize> for Object<'_> {
1170    fn from(x: usize) -> Self {
1171        let ptr = core::ptr::without_provenance(x);
1172        unsafe { i64::tag_ptr(ptr).into() }
1173    }
1174}
1175
1176impl TagType for usize {
1177    type Out = i64;
1178    fn tag(self) -> Gc<Self::Out> {
1179        TagType::tag(self as i64)
1180    }
1181}
1182
1183impl TagType for i32 {
1184    type Out = i64;
1185    fn tag(self) -> Gc<Self::Out> {
1186        TagType::tag(i64::from(self))
1187    }
1188}
1189
1190impl TagType for u32 {
1191    type Out = i64;
1192    fn tag(self) -> Gc<Self::Out> {
1193        TagType::tag(i64::from(self))
1194    }
1195}
1196
1197impl TagType for char {
1198    type Out = i64;
1199    fn tag(self) -> Gc<Self::Out> {
1200        TagType::tag(i64::from(self as u32))
1201    }
1202}
1203
1204impl TagType for u64 {
1205    type Out = i64;
1206    fn tag(self) -> Gc<Self::Out> {
1207        TagType::tag(self as i64)
1208    }
1209}
1210
1211impl TagType for u16 {
1212    type Out = i64;
1213    fn tag(self) -> Gc<Self::Out> {
1214        TagType::tag(i64::from(self))
1215    }
1216}
1217
1218impl From<i32> for Object<'_> {
1219    fn from(x: i32) -> Self {
1220        i64::from(x).into()
1221    }
1222}
1223
1224impl From<Object<'_>> for () {
1225    fn from(_: Object) {}
1226}
1227
1228pub(crate) type OptionalFlag = Option<()>;
1229
1230impl<'ob> TryFrom<Object<'ob>> for Number<'ob> {
1231    type Error = TypeError;
1232
1233    fn try_from(value: Object<'ob>) -> Result<Self, Self::Error> {
1234        match value.get_tag() {
1235            Tag::Int | Tag::Float => unsafe { Ok(cast_gc(value)) },
1236            _ => Err(TypeError::new(Type::Number, value)),
1237        }
1238    }
1239}
1240
1241impl<'ob> TryFrom<Object<'ob>> for Option<Number<'ob>> {
1242    type Error = TypeError;
1243
1244    fn try_from(value: Object<'ob>) -> Result<Self, Self::Error> {
1245        if value.is_nil() { Ok(None) } else { value.try_into().map(Some) }
1246    }
1247}
1248
1249impl<'ob> TryFrom<Object<'ob>> for List<'ob> {
1250    type Error = TypeError;
1251
1252    fn try_from(value: Object<'ob>) -> Result<Self, Self::Error> {
1253        match value.untag() {
1254            ObjectType::NIL | ObjectType::Cons(_) => unsafe { Ok(cast_gc(value)) },
1255            _ => Err(TypeError::new(Type::List, value)),
1256        }
1257    }
1258}
1259
1260impl<'ob> TryFrom<Function<'ob>> for Gc<&'ob Cons> {
1261    type Error = TypeError;
1262
1263    fn try_from(value: Function<'ob>) -> Result<Self, Self::Error> {
1264        match value.untag() {
1265            FunctionType::Cons(_) => unsafe { Ok(cast_gc(value)) },
1266            _ => Err(TypeError::new(Type::Cons, value)),
1267        }
1268    }
1269}
1270
1271impl<'ob> TryFrom<Object<'ob>> for Gc<Symbol<'ob>> {
1272    type Error = TypeError;
1273    fn try_from(value: Object<'ob>) -> Result<Self, Self::Error> {
1274        match value.untag() {
1275            ObjectType::Symbol(_) => unsafe { Ok(cast_gc(value)) },
1276            _ => Err(TypeError::new(Type::Symbol, value)),
1277        }
1278    }
1279}
1280
1281impl<'ob> TryFrom<Object<'ob>> for Function<'ob> {
1282    type Error = TypeError;
1283
1284    fn try_from(value: Object<'ob>) -> Result<Self, Self::Error> {
1285        match value.get_tag() {
1286            Tag::ByteFn | Tag::SubrFn | Tag::Cons | Tag::Symbol => unsafe { Ok(cast_gc(value)) },
1287            _ => Err(TypeError::new(Type::Func, value)),
1288        }
1289    }
1290}
1291
1292///////////////////////////
1293// Other implementations //
1294///////////////////////////
1295
1296impl<'ob> TryFrom<Object<'ob>> for Gc<i64> {
1297    type Error = TypeError;
1298
1299    fn try_from(value: Object<'ob>) -> Result<Self, Self::Error> {
1300        match value.get_tag() {
1301            Tag::Int => unsafe { Ok(cast_gc(value)) },
1302            _ => Err(TypeError::new(Type::Int, value)),
1303        }
1304    }
1305}
1306
1307// This function is needed due to the lack of specialization and there being a
1308// blanket impl for From<T> for Option<T>
1309impl<'ob> Object<'ob> {
1310    pub(crate) fn try_from_option<T, E>(value: Object<'ob>) -> Result<Option<T>, E>
1311    where
1312        Object<'ob>: TryInto<T, Error = E>,
1313    {
1314        if value.is_nil() { Ok(None) } else { Ok(Some(value.try_into()?)) }
1315    }
1316
1317    pub(crate) fn is_nil(self) -> bool {
1318        self == sym::NIL
1319    }
1320}
1321
1322impl<'ob> TryFrom<Object<'ob>> for Gc<&'ob Cons> {
1323    type Error = TypeError;
1324
1325    fn try_from(value: Object<'ob>) -> Result<Self, Self::Error> {
1326        match value.get_tag() {
1327            Tag::Cons => unsafe { Ok(cast_gc(value)) },
1328            _ => Err(TypeError::new(Type::Cons, value)),
1329        }
1330    }
1331}
1332
1333impl<'ob> TryFrom<Object<'ob>> for Gc<&'ob LispString> {
1334    type Error = TypeError;
1335
1336    fn try_from(value: Object<'ob>) -> Result<Self, Self::Error> {
1337        match value.get_tag() {
1338            Tag::String => unsafe { Ok(cast_gc(value)) },
1339            _ => Err(TypeError::new(Type::String, value)),
1340        }
1341    }
1342}
1343
1344impl<'ob> TryFrom<Object<'ob>> for Gc<&'ob ByteString> {
1345    type Error = TypeError;
1346
1347    fn try_from(value: Object<'ob>) -> Result<Self, Self::Error> {
1348        match value.get_tag() {
1349            Tag::ByteString => unsafe { Ok(cast_gc(value)) },
1350            _ => Err(TypeError::new(Type::String, value)),
1351        }
1352    }
1353}
1354
1355impl<'ob> TryFrom<Object<'ob>> for Gc<&'ob LispHashTable> {
1356    type Error = TypeError;
1357
1358    fn try_from(value: Object<'ob>) -> Result<Self, Self::Error> {
1359        match value.get_tag() {
1360            Tag::HashTable => unsafe { Ok(cast_gc(value)) },
1361            _ => Err(TypeError::new(Type::HashTable, value)),
1362        }
1363    }
1364}
1365
1366impl<'ob> TryFrom<Object<'ob>> for Gc<&'ob LispVec> {
1367    type Error = TypeError;
1368
1369    fn try_from(value: Object<'ob>) -> Result<Self, Self::Error> {
1370        match value.get_tag() {
1371            Tag::Vec => unsafe { Ok(cast_gc(value)) },
1372            _ => Err(TypeError::new(Type::Vec, value)),
1373        }
1374    }
1375}
1376
1377impl<'ob> TryFrom<Object<'ob>> for Gc<&'ob LispBuffer> {
1378    type Error = TypeError;
1379
1380    fn try_from(value: Object<'ob>) -> Result<Self, Self::Error> {
1381        match value.get_tag() {
1382            Tag::Buffer => unsafe { Ok(cast_gc(value)) },
1383            _ => Err(TypeError::new(Type::Buffer, value)),
1384        }
1385    }
1386}
1387
1388impl<'ob> TryFrom<Object<'ob>> for Gc<&'ob CharTable> {
1389    type Error = TypeError;
1390
1391    fn try_from(value: Object<'ob>) -> Result<Self, Self::Error> {
1392        match value.get_tag() {
1393            Tag::CharTable => unsafe { Ok(cast_gc(value)) },
1394            _ => Err(TypeError::new(Type::String, value)),
1395        }
1396    }
1397}
1398
1399impl<'ob> TryFrom<Object<'ob>> for Gc<&'ob LispBigInt> {
1400    type Error = TypeError;
1401
1402    fn try_from(value: Object<'ob>) -> Result<Self, Self::Error> {
1403        match value.get_tag() {
1404            Tag::BigInt => unsafe { Ok(cast_gc(value)) },
1405            _ => Err(TypeError::new(Type::String, value)),
1406        }
1407    }
1408}
1409
1410impl<'ob> std::ops::Deref for Gc<&'ob Cons> {
1411    type Target = Cons;
1412
1413    fn deref(&self) -> &'ob Self::Target {
1414        self.untag()
1415    }
1416}
1417
1418pub(crate) trait CloneIn<'new, T>
1419where
1420    T: 'new,
1421{
1422    fn clone_in<const C: bool>(&self, bk: &'new Block<C>) -> Gc<T>;
1423}
1424
1425impl<'new, T, U, E> CloneIn<'new, U> for Gc<T>
1426where
1427    // The WithLifetime bound ensures that T is the same type as U
1428    T: WithLifetime<'new, Out = U>,
1429    Gc<U>: TryFrom<Object<'new>, Error = E> + 'new,
1430{
1431    fn clone_in<const C: bool>(&self, bk: &'new Block<C>) -> Gc<U> {
1432        let obj = match self.as_obj().untag() {
1433            ObjectType::Int(x) => x.into(),
1434            ObjectType::Cons(x) => x.clone_in(bk).into(),
1435            ObjectType::String(x) => x.clone_in(bk).into(),
1436            ObjectType::ByteString(x) => x.clone_in(bk).into(),
1437            ObjectType::Symbol(x) => x.clone_in(bk).into(),
1438            ObjectType::ByteFn(x) => x.clone_in(bk).into(),
1439            ObjectType::SubrFn(x) => x.into(),
1440            ObjectType::Float(x) => x.clone_in(bk).into(),
1441            ObjectType::Vec(x) => x.clone_in(bk).into(),
1442            ObjectType::Record(x) => x.clone_in(bk).into(),
1443            ObjectType::HashTable(x) => x.clone_in(bk).into(),
1444            ObjectType::Buffer(x) => x.clone_in(bk).into(),
1445            ObjectType::CharTable(x) => x.clone_in(bk).into(),
1446            ObjectType::BigInt(x) => x.clone_in(bk).into(),
1447        };
1448        let Ok(x) = Gc::<U>::try_from(obj) else { unreachable!() };
1449        x
1450    }
1451}
1452
1453impl<T> GcMoveable for Gc<T>
1454where
1455    Self: Untag<T> + Copy,
1456    T: GcMoveable<Value = T> + TagType<Out = T>,
1457{
1458    type Value = Self;
1459
1460    fn move_value(&self, to_space: &bumpalo::Bump) -> Option<(Self::Value, bool)> {
1461        self.untag().move_value(to_space).map(|(x, moved)| (x.tag(), moved))
1462    }
1463}
1464
1465impl GcMoveable for Object<'_> {
1466    type Value = Self;
1467
1468    fn move_value(&self, to_space: &bumpalo::Bump) -> Option<(Self::Value, bool)> {
1469        let data = match self.untag() {
1470            ObjectType::Int(_) | ObjectType::SubrFn(_) | ObjectType::NIL => return None,
1471            ObjectType::Float(x) => cast_pair(x.move_value(to_space)?),
1472            ObjectType::Cons(x) => cast_pair(x.move_value(to_space)?),
1473            ObjectType::Vec(x) => cast_pair(x.move_value(to_space)?),
1474            ObjectType::Record(x) => cast_pair(x.move_value(to_space)?),
1475            ObjectType::HashTable(x) => cast_pair(x.move_value(to_space)?),
1476            ObjectType::String(x) => cast_pair(x.move_value(to_space)?),
1477            ObjectType::ByteString(x) => cast_pair(x.move_value(to_space)?),
1478            ObjectType::ByteFn(x) => cast_pair(x.move_value(to_space)?),
1479            ObjectType::Buffer(x) => cast_pair(x.move_value(to_space)?),
1480            ObjectType::Symbol(x) => {
1481                // Need to handle specially because a symbol is not a pointer,
1482                // but rather an offset
1483                let (sym, moved) = x.move_value(to_space)?;
1484                (sym.as_ptr(), moved)
1485            }
1486            ObjectType::CharTable(x) => cast_pair(x.move_value(to_space)?),
1487            ObjectType::BigInt(x) => cast_pair(x.move_value(to_space)?),
1488        };
1489
1490        let tag = self.get_tag();
1491        unsafe { Some((Object::from_ptr(data.0, tag), data.1)) }
1492    }
1493}
1494
1495impl GcMoveable for Function<'_> {
1496    type Value = Self;
1497
1498    fn move_value(&self, to_space: &bumpalo::Bump) -> Option<(Self::Value, bool)> {
1499        let data = match self.untag() {
1500            FunctionType::SubrFn(_) => return None,
1501            FunctionType::Cons(x) => cast_pair(x.move_value(to_space)?),
1502            FunctionType::ByteFn(x) => cast_pair(x.move_value(to_space)?),
1503            FunctionType::Symbol(x) => {
1504                let (sym, moved) = x.move_value(to_space)?;
1505                cast_pair((NonNull::from(sym.get()), moved))
1506            }
1507        };
1508
1509        let tag = self.get_tag();
1510        unsafe { Some((Function::from_ptr(data.0, tag), data.1)) }
1511    }
1512}
1513
1514impl GcMoveable for List<'_> {
1515    type Value = Self;
1516
1517    fn move_value(&self, to_space: &bumpalo::Bump) -> Option<(Self::Value, bool)> {
1518        let data = match self.untag() {
1519            ListType::Cons(x) => cast_pair(x.move_value(to_space)?),
1520            ListType::Nil => return None,
1521        };
1522
1523        let tag = self.get_tag();
1524        unsafe { Some((List::from_ptr(data.0, tag), data.1)) }
1525    }
1526}
1527
1528fn cast_pair<T>((ptr, moved): (NonNull<T>, bool)) -> (*const u8, bool) {
1529    (ptr.as_ptr().cast::<u8>(), moved)
1530}
1531
1532impl PartialEq<&str> for Object<'_> {
1533    fn eq(&self, other: &&str) -> bool {
1534        match self.untag() {
1535            ObjectType::String(x) => **x == **other,
1536            _ => false,
1537        }
1538    }
1539}
1540
1541impl PartialEq<char> for Object<'_> {
1542    fn eq(&self, other: &char) -> bool {
1543        match self.untag() {
1544            ObjectType::Int(x) => *other as i64 == x,
1545            _ => false,
1546        }
1547    }
1548}
1549
1550impl PartialEq<Symbol<'_>> for Object<'_> {
1551    fn eq(&self, other: &Symbol) -> bool {
1552        match self.untag() {
1553            ObjectType::Symbol(x) => x == *other,
1554            _ => false,
1555        }
1556    }
1557}
1558
1559impl PartialEq<f64> for Object<'_> {
1560    fn eq(&self, other: &f64) -> bool {
1561        use float_cmp::ApproxEq;
1562        match self.untag() {
1563            ObjectType::Float(x) => x.approx_eq(*other, (f64::EPSILON, 2)),
1564            _ => false,
1565        }
1566    }
1567}
1568
1569impl PartialEq<i64> for Object<'_> {
1570    fn eq(&self, other: &i64) -> bool {
1571        match self.untag() {
1572            ObjectType::Int(x) => x == *other,
1573            _ => false,
1574        }
1575    }
1576}
1577
1578impl PartialEq<bool> for Object<'_> {
1579    fn eq(&self, other: &bool) -> bool {
1580        if *other {
1581            matches!(self.untag(), ObjectType::Symbol(sym::TRUE))
1582        } else {
1583            matches!(self.untag(), ObjectType::Symbol(sym::NIL))
1584        }
1585    }
1586}
1587
1588impl<'ob> Object<'ob> {
1589    /// Convience method to easily match against cons cells that are the start
1590    /// of a list of values.
1591    pub(crate) fn as_cons_pair(self) -> Result<(Symbol<'ob>, ObjectType<'ob>), TypeError> {
1592        let cons: &Cons = self.try_into()?;
1593        let sym = cons.car().try_into()?;
1594        Ok((sym, cons.cdr().untag()))
1595    }
1596}
1597
1598impl<'ob> Function<'ob> {
1599    /// Convience method to easily match against cons cells that are the start
1600    /// of a list of values.
1601    pub(crate) fn as_cons_pair(self) -> Result<(Symbol<'ob>, FunctionType<'ob>), TypeError> {
1602        if let FunctionType::Cons(cons) = self.untag() {
1603            let sym = cons.car().try_into()?;
1604            let fun: Function = cons.cdr().try_into()?;
1605            Ok((sym, fun.untag()))
1606        } else {
1607            Err(TypeError::new(Type::Cons, self))
1608        }
1609    }
1610}
1611
1612impl Default for Object<'_> {
1613    fn default() -> Self {
1614        NIL
1615    }
1616}
1617
1618impl Default for List<'_> {
1619    fn default() -> Self {
1620        ListType::empty()
1621    }
1622}
1623
1624impl<T> fmt::Display for Gc<T> {
1625    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1626        write!(f, "{}", self.as_obj().untag())
1627    }
1628}
1629
1630impl<T> fmt::Debug for Gc<T> {
1631    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1632        write!(f, "{self}")
1633    }
1634}
1635
1636impl<T> PartialEq for Gc<T> {
1637    fn eq(&self, other: &Self) -> bool {
1638        self.ptr == other.ptr || self.as_obj().untag() == other.as_obj().untag()
1639    }
1640}
1641
1642impl<T> Eq for Gc<T> {}
1643
1644use std::hash::{Hash, Hasher};
1645impl<T> Hash for Gc<T> {
1646    fn hash<H: Hasher>(&self, state: &mut H) {
1647        self.ptr.hash(state);
1648    }
1649}
1650
1651impl fmt::Display for ObjectType<'_> {
1652    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1653        self.display_walk(f, &mut HashSet::default())
1654    }
1655}
1656
1657impl fmt::Debug for ObjectType<'_> {
1658    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1659        self.display_walk(f, &mut HashSet::default())
1660    }
1661}
1662
1663impl ObjectType<'_> {
1664    pub(crate) fn display_walk(
1665        &self,
1666        f: &mut fmt::Formatter,
1667        seen: &mut HashSet<*const u8>,
1668    ) -> fmt::Result {
1669        use fmt::Display as D;
1670        match self {
1671            ObjectType::Int(x) => D::fmt(x, f),
1672            ObjectType::Cons(x) => x.display_walk(f, seen),
1673            ObjectType::Vec(x) => x.display_walk(f, seen),
1674            ObjectType::Record(x) => x.display_walk(f, seen),
1675            ObjectType::HashTable(x) => x.display_walk(f, seen),
1676            ObjectType::String(x) => write!(f, "\"{x}\""),
1677            ObjectType::ByteString(x) => write!(f, "\"{x}\""),
1678            ObjectType::Symbol(x) => D::fmt(x, f),
1679            ObjectType::ByteFn(x) => D::fmt(x, f),
1680            ObjectType::SubrFn(x) => D::fmt(x, f),
1681            ObjectType::Float(x) => D::fmt(x, f),
1682            ObjectType::Buffer(x) => D::fmt(x, f),
1683            ObjectType::CharTable(x) => D::fmt(x, f),
1684            ObjectType::BigInt(x) => D::fmt(x, f),
1685        }
1686    }
1687}
1688
1689#[cfg(test)]
1690mod test {
1691    use super::{MAX_FIXNUM, MIN_FIXNUM, TagType};
1692    use crate::core::gc::{Context, RootSet};
1693    use rune_core::macros::list;
1694
1695    #[test]
1696    fn test_clamp_fixnum() {
1697        assert_eq!(0i64.tag().untag(), 0);
1698        assert_eq!((-1_i64).tag().untag(), -1);
1699        assert_eq!(i64::MAX.tag().untag(), MAX_FIXNUM);
1700        assert_eq!(MAX_FIXNUM.tag().untag(), MAX_FIXNUM);
1701        assert_eq!(i64::MIN.tag().untag(), MIN_FIXNUM);
1702        assert_eq!(MIN_FIXNUM.tag().untag(), MIN_FIXNUM);
1703    }
1704
1705    #[test]
1706    fn test_print_circle() {
1707        let roots = &RootSet::default();
1708        let cx = &Context::new(roots);
1709        let cons = list![1; cx];
1710        cons.unwrap_cons().set_cdr(cons).unwrap();
1711        assert_eq!(format!("{cons}"), "(1 . #0)");
1712
1713        cons.unwrap_cons().set_car(cons).unwrap();
1714        assert_eq!(format!("{cons}"), "(#0 . #0)");
1715    }
1716}