Skip to main content

turbo_tasks/vc/
resolved.rs

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