turbo_tasks/vc/
resolved.rs

1use std::{
2    any::Any,
3    fmt::Debug,
4    future::IntoFuture,
5    hash::{Hash, Hasher},
6    marker::PhantomData,
7    mem::transmute,
8    ops::Deref,
9};
10
11use anyhow::Result;
12use serde::{Deserialize, Serialize};
13
14use crate::{
15    RawVc, Upcast, VcRead, VcTransparentRead, VcValueTrait, VcValueType,
16    debug::{ValueDebug, ValueDebugFormat, ValueDebugFormatString},
17    trace::{TraceRawVcs, TraceRawVcsContext},
18    vc::Vc,
19};
20
21/// A "subtype" (via [`Deref`]) of [`Vc`] that represents a specific [`Vc::cell`]/`.cell()` or
22/// [`ResolvedVc::cell`]/`.resolved_cell()` constructor call within [a task][macro@crate::function].
23///
24/// Unlike [`Vc`], `ResolvedVc`:
25///
26/// - Does not potentially refer to task-local information, meaning that it implements
27///   [`NonLocalValue`], and can be used in any [`#[turbo_tasks::value]`][macro@crate::value].
28///
29/// - Has only one potential internal representation, meaning that it has a saner equality
30///   definition.
31///
32/// - Points to a concrete value with a type, and is therefore [cheap to
33///   downcast][ResolvedVc::try_downcast].
34///
35///
36/// ## Construction
37///
38/// There are a few ways to construct a `ResolvedVc`, in order of preference:
39///
40/// 1. Given a [value][VcValueType], construct a `ResolvedVc` using [`ResolvedVc::cell`] (for
41///    "transparent" values) or by calling the generated `.resolved_cell()` constructor on the value
42///    type.
43///
44/// 2. Given an argument to a function using the [`#[turbo_tasks::function]`][macro@crate::function]
45///    macro, change the argument's type to a `ResolvedVc`. The [rewritten external signature] will
46///    still use [`Vc`], but when the function is called, the [`Vc`] will be resolved.
47///
48/// 3. Given a [`Vc`], use [`.to_resolved().await?`][Vc::to_resolved].
49///
50///
51/// ## Equality & Hashing
52///
53/// Equality between two `ResolvedVc`s means that both have an identical in-memory representation
54/// and point to the same cell. The implementation of [`Hash`] has similar behavior.
55///
56/// If `.await`ed at the same time, both would likely resolve to the same [`ReadRef`], though it is
57/// possible that they may not if the cell is invalidated between `.await`s.
58///
59/// Because equality is a synchronous operation that cannot read the cell contents, even if the
60/// `ResolvedVc`s are not equal, it is possible that if `.await`ed, both `ResolvedVc`s could point
61/// to the same or equal values.
62///
63///
64/// [`NonLocalValue`]: crate::NonLocalValue
65/// [rewritten external signature]: https://turbopack-rust-docs.vercel.sh/turbo-engine/tasks.html#external-signature-rewriting
66/// [`ReadRef`]: crate::ReadRef
67#[derive(Serialize, Deserialize)]
68#[serde(transparent, bound = "")]
69#[repr(transparent)]
70pub struct ResolvedVc<T>
71where
72    T: ?Sized,
73{
74    pub(crate) node: Vc<T>,
75}
76
77impl<T> ResolvedVc<T>
78where
79    T: ?Sized,
80{
81    /// This function exists to intercept calls to Vc::to_resolved through dereferencing
82    /// a ResolvedVc. Converting to Vc and re-resolving it puts unnecessary stress on
83    /// the turbo tasks engine.
84    #[deprecated(note = "No point in resolving a vc that is already resolved")]
85    pub async fn to_resolved(self) -> Result<Self> {
86        Ok(self)
87    }
88    #[deprecated(note = "No point in resolving a vc that is already resolved")]
89    pub async fn resolve(self) -> Result<Vc<T>> {
90        Ok(self.node)
91    }
92}
93
94impl<T> Copy for ResolvedVc<T> where T: ?Sized {}
95
96impl<T> Clone for ResolvedVc<T>
97where
98    T: ?Sized,
99{
100    fn clone(&self) -> Self {
101        *self
102    }
103}
104
105impl<T> Deref for ResolvedVc<T>
106where
107    T: ?Sized,
108{
109    type Target = Vc<T>;
110
111    fn deref(&self) -> &Self::Target {
112        &self.node
113    }
114}
115
116impl<T> PartialEq<ResolvedVc<T>> for ResolvedVc<T>
117where
118    T: ?Sized,
119{
120    fn eq(&self, other: &Self) -> bool {
121        self.node == other.node
122    }
123}
124
125impl<T> Eq for ResolvedVc<T> where T: ?Sized {}
126
127impl<T> Hash for ResolvedVc<T>
128where
129    T: ?Sized,
130{
131    fn hash<H: Hasher>(&self, state: &mut H) {
132        self.node.hash(state);
133    }
134}
135
136impl<T, Inner, Repr> Default for ResolvedVc<T>
137where
138    T: VcValueType<Read = VcTransparentRead<T, Inner, Repr>>,
139    Inner: Any + Send + Sync + Default,
140    Repr: VcValueType,
141{
142    fn default() -> Self {
143        Self::cell(Default::default())
144    }
145}
146
147macro_rules! into_future {
148    ($ty:ty) => {
149        impl<T> IntoFuture for $ty
150        where
151            T: VcValueType,
152        {
153            type Output = <Vc<T> as IntoFuture>::Output;
154            type IntoFuture = <Vc<T> as IntoFuture>::IntoFuture;
155            fn into_future(self) -> Self::IntoFuture {
156                (*self).into_future()
157            }
158        }
159    };
160}
161
162into_future!(ResolvedVc<T>);
163into_future!(&ResolvedVc<T>);
164into_future!(&mut ResolvedVc<T>);
165
166impl<T> ResolvedVc<T>
167where
168    T: VcValueType,
169{
170    // called by the `.resolved_cell()` method generated by the `#[turbo_tasks::value]` macro
171    #[doc(hidden)]
172    pub fn cell_private(inner: <T::Read as VcRead<T>>::Target) -> Self {
173        Self {
174            node: Vc::<T>::cell_private(inner),
175        }
176    }
177}
178
179impl<T, Inner, Repr> ResolvedVc<T>
180where
181    T: VcValueType<Read = VcTransparentRead<T, Inner, Repr>>,
182    Inner: Any + Send + Sync,
183    Repr: VcValueType,
184{
185    pub fn cell(inner: Inner) -> Self {
186        Self {
187            node: Vc::<T>::cell(inner),
188        }
189    }
190}
191
192impl<T> ResolvedVc<T>
193where
194    T: ?Sized,
195{
196    /// Upcasts the given `ResolvedVc<T>` to a `ResolvedVc<Box<dyn K>>`.
197    ///
198    /// See also: [`Vc::upcast`].
199    #[inline(always)]
200    pub fn upcast<K>(this: Self) -> ResolvedVc<K>
201    where
202        T: Upcast<K>,
203        K: VcValueTrait + ?Sized,
204    {
205        ResolvedVc {
206            node: Vc::upcast(this.node),
207        }
208    }
209
210    /// Cheaply converts a Vec of resolved Vcs to a Vec of Vcs.
211    pub fn deref_vec(vec: Vec<ResolvedVc<T>>) -> Vec<Vc<T>> {
212        debug_assert!(size_of::<ResolvedVc<T>>() == size_of::<Vc<T>>());
213        // Safety: The memory layout of `ResolvedVc<T>` and `Vc<T>` is the same.
214        unsafe { transmute::<Vec<ResolvedVc<T>>, Vec<Vc<T>>>(vec) }
215    }
216
217    /// Cheaply converts a slice of resolved Vcs to a slice of Vcs.
218    pub fn deref_slice(slice: &[ResolvedVc<T>]) -> &[Vc<T>] {
219        debug_assert!(size_of::<ResolvedVc<T>>() == size_of::<Vc<T>>());
220        // Safety: The memory layout of `ResolvedVc<T>` and `Vc<T>` is the same.
221        unsafe { transmute::<&[ResolvedVc<T>], &[Vc<T>]>(slice) }
222    }
223}
224
225impl<T> ResolvedVc<T>
226where
227    T: VcValueTrait + ?Sized,
228{
229    /// Returns `None` if the underlying value type does not implement `K`.
230    ///
231    /// **Note:** if the trait `T` is required to implement `K`, use [`ResolvedVc::upcast`] instead.
232    /// This provides stronger guarantees, removing the need for a [`Result`] return type.
233    ///
234    /// See also: [`Vc::try_resolve_sidecast`].
235    pub fn try_sidecast<K>(this: Self) -> Option<ResolvedVc<K>>
236    where
237        K: VcValueTrait + ?Sized,
238    {
239        // `RawVc::TaskCell` already contains all the type information needed to check this
240        // sidecast, so we don't need to read the underlying cell!
241        let raw_vc = this.node.node;
242        raw_vc
243            .resolved_has_trait(<K as VcValueTrait>::get_trait_type_id())
244            .then_some(ResolvedVc {
245                node: Vc {
246                    node: raw_vc,
247                    _t: PhantomData,
248                },
249            })
250    }
251
252    /// Attempts to downcast the given `ResolvedVc<Box<dyn T>>` to a `ResolvedVc<K>`, where `K`
253    /// is of the form `Box<dyn L>`, and `L` is a value trait.
254    ///
255    /// Returns `None` if the underlying value type is not a `K`.
256    ///
257    /// See also: [`Vc::try_resolve_downcast`].
258    pub fn try_downcast<K>(this: Self) -> Option<ResolvedVc<K>>
259    where
260        K: Upcast<T> + VcValueTrait + ?Sized,
261    {
262        // this is just a more type-safe version of a sidecast
263        Self::try_sidecast(this)
264    }
265
266    /// Attempts to downcast the given `Vc<Box<dyn T>>` to a `Vc<K>`, where `K` is a value type.
267    ///
268    /// Returns `None` if the underlying value type is not a `K`.
269    ///
270    /// See also: [`Vc::try_resolve_downcast_type`].
271    pub fn try_downcast_type<K>(this: Self) -> Option<ResolvedVc<K>>
272    where
273        K: Upcast<T> + VcValueType,
274    {
275        let raw_vc = this.node.node;
276        raw_vc
277            .resolved_is_type(<K as VcValueType>::get_value_type_id())
278            .then_some(ResolvedVc {
279                node: Vc {
280                    node: raw_vc,
281                    _t: PhantomData,
282                },
283            })
284    }
285}
286
287/// Generates an opaque debug representation of the [`ResolvedVc`] itself, but not the data inside
288/// of it.
289///
290/// This is implemented to allow types containing [`ResolvedVc`] to implement the synchronous
291/// [`Debug`] trait, but in most cases users should use the [`ValueDebug`] implementation to get a
292/// string representation of the contents of the cell.
293impl<T> Debug for ResolvedVc<T>
294where
295    T: ?Sized,
296{
297    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
298        f.debug_struct("ResolvedVc")
299            .field("node", &self.node.node)
300            .finish()
301    }
302}
303
304impl<T> TraceRawVcs for ResolvedVc<T>
305where
306    T: ?Sized,
307{
308    fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
309        TraceRawVcs::trace_raw_vcs(&self.node, trace_context);
310    }
311}
312
313impl<T> ValueDebugFormat for ResolvedVc<T>
314where
315    T: Upcast<Box<dyn ValueDebug>> + Send + Sync + ?Sized,
316{
317    fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString {
318        self.node.value_debug_format(depth)
319    }
320}
321
322impl<T> TryFrom<RawVc> for ResolvedVc<T>
323where
324    T: ?Sized,
325{
326    type Error = anyhow::Error;
327
328    fn try_from(raw: RawVc) -> Result<Self> {
329        if !matches!(raw, RawVc::TaskCell(..)) {
330            anyhow::bail!("Given RawVc {raw:?} is not a TaskCell");
331        }
332        Ok(Self {
333            node: Vc::from(raw),
334        })
335    }
336}