turbo_tasks/
trait_ref.rs

1use std::{fmt::Debug, future::Future, marker::PhantomData};
2
3use anyhow::Result;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7    Vc, VcValueTrait,
8    registry::get_value_type,
9    task::shared_reference::TypedSharedReference,
10    vc::{ReadVcFuture, VcValueTraitCast, cast::VcCast},
11};
12
13/// Similar to a [`ReadRef<T>`][crate::ReadRef], but contains a value trait
14/// object instead.
15///
16/// The only way to interact with a `TraitRef<T>` is by passing
17/// it around or turning it back into a value trait vc by calling
18/// [`ReadRef::cell`][crate::ReadRef::cell].
19///
20/// Internally it stores a reference counted reference to a value on the heap.
21pub struct TraitRef<T>
22where
23    T: ?Sized,
24{
25    shared_reference: TypedSharedReference,
26    _t: PhantomData<T>,
27}
28
29impl<T> Debug for TraitRef<T> {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        f.debug_struct("TraitRef")
32            .field("shared_reference", &self.shared_reference)
33            .finish()
34    }
35}
36
37impl<T> Clone for TraitRef<T> {
38    fn clone(&self) -> Self {
39        Self {
40            shared_reference: self.shared_reference.clone(),
41            _t: PhantomData,
42        }
43    }
44}
45
46impl<T> PartialEq for TraitRef<T> {
47    fn eq(&self, other: &Self) -> bool {
48        self.shared_reference == other.shared_reference
49    }
50}
51
52impl<T> Eq for TraitRef<T> {}
53
54impl<T> std::hash::Hash for TraitRef<T> {
55    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
56        self.shared_reference.hash(state)
57    }
58}
59
60impl<T> Serialize for TraitRef<T> {
61    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
62        self.shared_reference.serialize(serializer)
63    }
64}
65
66impl<'de, T> Deserialize<'de> for TraitRef<T> {
67    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
68        Ok(Self {
69            shared_reference: TypedSharedReference::deserialize(deserializer)?,
70            _t: PhantomData,
71        })
72    }
73}
74
75// This is a workaround for https://github.com/rust-lang/rust-analyzer/issues/19971
76// that ensures type inference keeps working with ptr_metadata.
77
78#[cfg(rust_analyzer)]
79impl<U> std::ops::Deref for TraitRef<Box<U>>
80where
81    U: ?Sized,
82    Box<U>: VcValueTrait,
83{
84    type Target = U;
85
86    fn deref(&self) -> &Self::Target {
87        unimplemented!("only exists for rust-analyzer type inference")
88    }
89}
90
91#[cfg(not(rust_analyzer))]
92impl<U> std::ops::Deref for TraitRef<Box<U>>
93where
94    Box<U>: VcValueTrait<ValueTrait = U>,
95    U: std::ptr::Pointee<Metadata = std::ptr::DynMetadata<U>> + ?Sized,
96{
97    type Target = U;
98
99    fn deref(&self) -> &Self::Target {
100        // This lookup will fail if the value type stored does not actually implement the trait,
101        // which implies a bug in either the registry code or the macro code.
102        let metadata =
103            <Box<U> as VcValueTrait>::get_impl_vtables().get(self.shared_reference.type_id);
104        let downcast_ptr = std::ptr::from_raw_parts(
105            self.shared_reference.reference.0.as_ptr() as *const (),
106            metadata,
107        );
108        unsafe { &*downcast_ptr }
109    }
110}
111
112// Otherwise, TraitRef<Box<dyn Trait>> would not be Sync.
113// SAFETY: TraitRef doesn't actually contain a T.
114unsafe impl<T> Sync for TraitRef<T> where T: ?Sized {}
115
116// Otherwise, TraitRef<Box<dyn Trait>> would not be Send.
117// SAFETY: TraitRef doesn't actually contain a T.
118unsafe impl<T> Send for TraitRef<T> where T: ?Sized {}
119
120impl<T> Unpin for TraitRef<T> where T: ?Sized {}
121
122impl<T> TraitRef<T>
123where
124    T: ?Sized,
125{
126    pub(crate) fn new(shared_reference: TypedSharedReference) -> Self {
127        Self {
128            shared_reference,
129            _t: PhantomData,
130        }
131    }
132
133    pub fn ptr_eq(this: &Self, other: &Self) -> bool {
134        triomphe::Arc::ptr_eq(
135            &this.shared_reference.reference.0,
136            &other.shared_reference.reference.0,
137        )
138    }
139}
140
141impl<T> TraitRef<T>
142where
143    T: VcValueTrait + ?Sized,
144{
145    /// Returns a new cell that points to a value that implements the value
146    /// trait `T`.
147    pub fn cell(trait_ref: TraitRef<T>) -> Vc<T> {
148        let TraitRef {
149            shared_reference, ..
150        } = trait_ref;
151        let value_type = get_value_type(shared_reference.type_id);
152        (value_type.raw_cell)(shared_reference).into()
153    }
154}
155
156/// A trait that allows a value trait vc to be converted into a trait reference.
157///
158/// The signature is similar to `IntoFuture`, but we don't want trait vcs to
159/// have the same future-like semantics as value vcs when it comes to producing
160/// refs. This behavior is rarely needed, so in most cases, `.await`ing a trait
161/// vc is a mistake.
162pub trait IntoTraitRef {
163    type ValueTrait: VcValueTrait + ?Sized;
164    type Future: Future<Output = Result<<VcValueTraitCast<Self::ValueTrait> as VcCast>::Output>>;
165
166    fn into_trait_ref(self) -> Self::Future;
167}
168
169impl<T> IntoTraitRef for Vc<T>
170where
171    T: VcValueTrait + ?Sized,
172{
173    type ValueTrait = T;
174
175    type Future = ReadVcFuture<T, VcValueTraitCast<T>>;
176
177    fn into_trait_ref(self) -> Self::Future {
178        self.node.into_read().into()
179    }
180}