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