diff --git a/turbopack/crates/turbo-tasks-testing/tests/resolved_vc.rs b/turbopack/crates/turbo-tasks-testing/tests/resolved_vc.rs index e8cf8e3ef35bf..0222b91a44a7d 100644 --- a/turbopack/crates/turbo-tasks-testing/tests/resolved_vc.rs +++ b/turbopack/crates/turbo-tasks-testing/tests/resolved_vc.rs @@ -77,3 +77,35 @@ async fn test_into_future() -> Result<()> { }) .await } + +#[tokio::test] +async fn test_sidecast() -> Result<()> { + run(®ISTRATION, || async { + let concrete_value = ImplementsAandB.resolved_cell(); + let as_a = ResolvedVc::upcast::>(concrete_value); + let as_b = ResolvedVc::try_sidecast_sync::>(as_a); + assert!(as_b.is_some()); + let as_c = ResolvedVc::try_sidecast_sync::>(as_a); + assert!(as_c.is_none()); + Ok(()) + }) + .await +} + +#[turbo_tasks::value_trait] +trait TraitA {} + +#[turbo_tasks::value_trait] +trait TraitB {} + +#[turbo_tasks::value_trait] +trait TraitC {} + +#[turbo_tasks::value] +struct ImplementsAandB; + +#[turbo_tasks::value_impl] +impl TraitA for ImplementsAandB {} + +#[turbo_tasks::value_impl] +impl TraitB for ImplementsAandB {} diff --git a/turbopack/crates/turbo-tasks/src/raw_vc.rs b/turbopack/crates/turbo-tasks/src/raw_vc.rs index 12988e30b8871..1e0cf66b3ab43 100644 --- a/turbopack/crates/turbo-tasks/src/raw_vc.rs +++ b/turbopack/crates/turbo-tasks/src/raw_vc.rs @@ -288,6 +288,17 @@ impl RawVc { } } } + + /// For a type that's already resolved, synchronously check if it implements a trait using the + /// type information in `RawVc::TaskCell` (we don't actualy need to read the cell!). + pub(crate) fn resolved_has_trait(&self, trait_id: TraitTypeId) -> bool { + match self { + RawVc::TaskCell(_task_id, cell_id) => { + get_value_type(cell_id.type_id).has_trait(&trait_id) + } + _ => unreachable!("resolved_has_trait must be called with a RawVc::TaskCell"), + } + } } impl CollectiblesSource for RawVc { diff --git a/turbopack/crates/turbo-tasks/src/vc/resolved.rs b/turbopack/crates/turbo-tasks/src/vc/resolved.rs index 83e64aed7ea58..1918ddb82f2bb 100644 --- a/turbopack/crates/turbo-tasks/src/vc/resolved.rs +++ b/turbopack/crates/turbo-tasks/src/vc/resolved.rs @@ -3,6 +3,7 @@ use std::{ fmt::Debug, future::IntoFuture, hash::{Hash, Hasher}, + marker::PhantomData, ops::Deref, }; @@ -215,10 +216,33 @@ where where K: VcValueTrait + ?Sized, { - // must be async, as we must read the cell to determine the type - Ok(Vc::try_resolve_sidecast(this.node) - .await? - .map(|node| ResolvedVc { node })) + // TODO: Expose a synchronous API instead of this async one that returns `Result>` + Ok(Self::try_sidecast_sync(this)) + } + + /// Attempts to sidecast the given `ResolvedVc>` to a `ResolvedVc>`. + /// + /// Returns `None` if the underlying value type does not implement `K`. + /// + /// **Note:** if the trait `T` is required to implement `K`, use [`ResolvedVc::upcast`] instead. + /// This provides stronger guarantees, removing the need for a [`Result`] return type. + /// + /// See also: [`Vc::try_resolve_sidecast`]. + pub fn try_sidecast_sync(this: Self) -> Option> + where + K: VcValueTrait + ?Sized, + { + // `RawVc::TaskCell` already contains all the type information needed to check this + // sidecast, so we don't need to read the underlying cell! + let raw_vc = this.node.node; + raw_vc + .resolved_has_trait(::get_trait_type_id()) + .then_some(ResolvedVc { + node: Vc { + node: raw_vc, + _t: PhantomData, + }, + }) } /// Attempts to downcast the given `ResolvedVc>` to a `ResolvedVc`, where `K`