Skip to main content

rune/core/object/
tagged.rs

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