Skip to main content

turbo_tasks/
trait_ref.rs

1use std::{fmt::Debug, marker::PhantomData};
2
3use crate::{
4    Vc, VcValueTrait, registry::get_value_type, task::shared_reference::TypedSharedReference,
5    vc::UpcastStrict,
6};
7
8/// Similar to a [`ReadRef<T>`][crate::ReadRef], but contains a value trait object instead.
9///
10/// Non-turbo-task methods with a `&self` receiver can be called on this reference.
11///
12/// A `TraitRef<T>` can be turned back into a value trait vc by calling [`TraitRef::cell`].
13///
14/// Internally it stores a reference counted reference to a value on the heap.
15pub struct TraitRef<T>
16where
17    T: ?Sized,
18{
19    shared_reference: TypedSharedReference,
20    _t: PhantomData<T>,
21}
22
23impl<T> Debug for TraitRef<T> {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        f.debug_struct("TraitRef")
26            .field("shared_reference", &self.shared_reference)
27            .finish()
28    }
29}
30
31impl<T> Clone for TraitRef<T> {
32    fn clone(&self) -> Self {
33        Self {
34            shared_reference: self.shared_reference.clone(),
35            _t: PhantomData,
36        }
37    }
38}
39
40impl<T> PartialEq for TraitRef<T> {
41    fn eq(&self, other: &Self) -> bool {
42        self.shared_reference == other.shared_reference
43    }
44}
45
46impl<T> Eq for TraitRef<T> {}
47
48impl<T> std::hash::Hash for TraitRef<T> {
49    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
50        self.shared_reference.hash(state)
51    }
52}
53
54impl<U> std::ops::Deref for TraitRef<Box<U>>
55where
56    Box<U>: VcValueTrait<ValueTrait = U>,
57    U: std::ptr::Pointee<Metadata = std::ptr::DynMetadata<U>> + ?Sized,
58{
59    type Target = U;
60
61    fn deref(&self) -> &Self::Target {
62        // This lookup will fail if the value type stored does not actually implement the trait,
63        // which implies a bug in either the registry code or the macro code.
64        let downcast_ptr = <Box<U> as VcValueTrait>::IMPL_VTABLES.cast(
65            self.shared_reference.type_id,
66            self.shared_reference.reference.0.as_ptr() as *const (),
67        );
68        // SAFETY: the pointer is derived from an Arc
69        unsafe { &*downcast_ptr }
70    }
71}
72
73// Otherwise, TraitRef<Box<dyn Trait>> would not be Sync.
74// SAFETY: TraitRef doesn't actually contain a T.
75unsafe impl<T> Sync for TraitRef<T> where T: ?Sized {}
76
77// Otherwise, TraitRef<Box<dyn Trait>> would not be Send.
78// SAFETY: TraitRef doesn't actually contain a T.
79unsafe impl<T> Send for TraitRef<T> where T: ?Sized {}
80
81impl<T> Unpin for TraitRef<T> where T: ?Sized {}
82
83impl<T> TraitRef<T>
84where
85    T: ?Sized,
86{
87    pub(crate) fn new(shared_reference: TypedSharedReference) -> Self {
88        Self {
89            shared_reference,
90            _t: PhantomData,
91        }
92    }
93
94    pub fn ptr_eq(this: &Self, other: &Self) -> bool {
95        triomphe::Arc::ptr_eq(
96            &this.shared_reference.reference.0,
97            &other.shared_reference.reference.0,
98        )
99    }
100}
101
102impl<T> TraitRef<T>
103where
104    T: VcValueTrait + ?Sized,
105{
106    /// Returns a new cell that points to a value that implements the value
107    /// trait `T`.
108    pub fn cell(trait_ref: TraitRef<T>) -> Vc<T> {
109        let TraitRef {
110            shared_reference, ..
111        } = trait_ref;
112        let value_type = get_value_type(shared_reference.type_id);
113        (value_type.raw_cell)(shared_reference).into()
114    }
115
116    /// Attempts to downcast this trait reference to a sub-trait `K`, where `K`
117    /// is of the form `Box<dyn L>` and `L: T` is a value trait.
118    ///
119    /// [`ResolvedVc::try_downcast`]: crate::ResolvedVc::try_downcast
120    /// [`ResolvedVc`]: crate::ResolvedVc
121    pub fn try_downcast<K>(this: TraitRef<T>) -> Option<TraitRef<K>>
122    where
123        K: UpcastStrict<T> + VcValueTrait + ?Sized,
124    {
125        get_value_type(this.shared_reference.type_id)
126            .has_trait(&<K as VcValueTrait>::get_trait_type_id())
127            .then(|| TraitRef::new(this.shared_reference))
128    }
129}