Skip to main content

turbo_tasks/vc/
resolved.rs

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