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, UpcastStrict, 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: 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 /// Cheaply converts a Vec of resolved Vcs to a Vec of Vcs.
225 pub fn deref_vec(vec: Vec<ResolvedVc<T>>) -> Vec<Vc<T>> {
226 debug_assert!(size_of::<ResolvedVc<T>>() == size_of::<Vc<T>>());
227 // Safety: The memory layout of `ResolvedVc<T>` and `Vc<T>` is the same.
228 unsafe { transmute::<Vec<ResolvedVc<T>>, Vec<Vc<T>>>(vec) }
229 }
230
231 /// Cheaply converts a slice of resolved Vcs to a slice of Vcs.
232 pub fn deref_slice(slice: &[ResolvedVc<T>]) -> &[Vc<T>] {
233 debug_assert!(size_of::<ResolvedVc<T>>() == size_of::<Vc<T>>());
234 // Safety: The memory layout of `ResolvedVc<T>` and `Vc<T>` is the same.
235 unsafe { transmute::<&[ResolvedVc<T>], &[Vc<T>]>(slice) }
236 }
237}
238
239impl<T> ResolvedVc<T>
240where
241 T: VcValueTrait + ?Sized,
242{
243 /// Returns `None` if the underlying value type does not implement `K`.
244 ///
245 /// **Note:** if the trait `T` is required to implement `K`, use [`ResolvedVc::upcast`] instead.
246 /// That method provides stronger guarantees, removing the need for a [`Option`] return type.
247 ///
248 /// See also: [`Vc::try_resolve_sidecast`].
249 pub fn try_sidecast<K>(this: Self) -> Option<ResolvedVc<K>>
250 where
251 K: VcValueTrait + ?Sized,
252 {
253 // Runtime assertion to catch K == T cases with a clear error message
254 // This will be optimized away in release builds but helps during development
255 // We use trait type IDs since T and K might be trait objects (?Sized)
256 debug_assert!(
257 <K as VcValueTrait>::get_trait_type_id() != <T as VcValueTrait>::get_trait_type_id(),
258 "Attempted to cast a type {} to itself, which is pointless. Use the value directly \
259 instead.",
260 crate::registry::get_trait(<T as VcValueTrait>::get_trait_type_id()).global_name
261 );
262 // `RawVc::TaskCell` already contains all the type information needed to check this
263 // sidecast, so we don't need to read the underlying cell!
264 let raw_vc = this.node.node;
265 raw_vc
266 .resolved_has_trait(<K as VcValueTrait>::get_trait_type_id())
267 .then_some(ResolvedVc {
268 node: Vc {
269 node: raw_vc,
270 _t: PhantomData,
271 },
272 })
273 }
274
275 /// Attempts to downcast the given `ResolvedVc<Box<dyn T>>` to a `ResolvedVc<K>`, where `K`
276 /// is of the form `Box<dyn L>`, and `L` is a value trait.
277 ///
278 /// Returns `None` if the underlying value type is not a `K`.
279 ///
280 /// See also: [`Vc::try_resolve_downcast`].
281 pub fn try_downcast<K>(this: Self) -> Option<ResolvedVc<K>>
282 where
283 K: UpcastStrict<T> + VcValueTrait + ?Sized,
284 {
285 // this is just a more type-safe version of a sidecast
286 Self::try_sidecast(this)
287 }
288
289 /// Attempts to downcast the given `Vc<Box<dyn T>>` to a `Vc<K>`, where `K` is a value type.
290 ///
291 /// Returns `None` if the underlying value type is not a `K`.
292 ///
293 /// See also: [`Vc::try_resolve_downcast_type`].
294 pub fn try_downcast_type<K>(this: Self) -> Option<ResolvedVc<K>>
295 where
296 K: UpcastStrict<T> + VcValueType,
297 {
298 let raw_vc = this.node.node;
299 raw_vc
300 .resolved_is_type(<K as VcValueType>::get_value_type_id())
301 .then_some(ResolvedVc {
302 node: Vc {
303 node: raw_vc,
304 _t: PhantomData,
305 },
306 })
307 }
308}
309
310/// Generates an opaque debug representation of the [`ResolvedVc`] itself, but not the data inside
311/// of it.
312///
313/// This is implemented to allow types containing [`ResolvedVc`] to implement the synchronous
314/// [`Debug`] trait, but in most cases users should use the [`ValueDebug`] implementation to get a
315/// string representation of the contents of the cell.
316impl<T> Debug for ResolvedVc<T>
317where
318 T: ?Sized,
319{
320 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
321 f.debug_tuple("ResolvedVc").field(&self.node.node).finish()
322 }
323}
324
325impl<T> TraceRawVcs for ResolvedVc<T>
326where
327 T: ?Sized,
328{
329 fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext) {
330 TraceRawVcs::trace_raw_vcs(&self.node, trace_context);
331 }
332}
333
334impl<T> ValueDebugFormat for ResolvedVc<T>
335where
336 T: UpcastStrict<Box<dyn ValueDebug>> + Send + Sync + ?Sized,
337{
338 fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString<'_> {
339 self.node.value_debug_format(depth)
340 }
341}
342
343impl<T> TryFrom<RawVc> for ResolvedVc<T>
344where
345 T: ?Sized,
346{
347 type Error = anyhow::Error;
348
349 fn try_from(raw: RawVc) -> Result<Self> {
350 if !matches!(raw, RawVc::TaskCell(..)) {
351 anyhow::bail!("Given RawVc {raw:?} is not a TaskCell");
352 }
353 Ok(Self {
354 node: Vc::from(raw),
355 })
356 }
357}