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