diff --git a/turbopack/crates/turbo-tasks/src/lib.rs b/turbopack/crates/turbo-tasks/src/lib.rs index 2339efc6a7042..a30a0b25a5e5d 100644 --- a/turbopack/crates/turbo-tasks/src/lib.rs +++ b/turbopack/crates/turbo-tasks/src/lib.rs @@ -71,7 +71,7 @@ pub use anyhow::{Error, Result}; use auto_hash_map::AutoSet; use rustc_hash::FxHasher; pub use shrink_to_fit::ShrinkToFit; -pub use turbo_tasks_macros::{turbobail, turbofmt, value_impl}; +pub use turbo_tasks_macros::{turbobail, turbofmt}; pub use crate::{ capture_future::TurboTasksPanic, @@ -249,27 +249,89 @@ pub use turbo_tasks_macros::function; #[rustfmt::skip] pub use turbo_tasks_macros::value; -/// Allows this trait to be used as part of a trait object inside of a value -/// cell, in the form of `Vc`. +/// Allows this trait to be used as part of a trait object inside of a value cell, in the form of +/// `Vc>`. The annotated trait is made into a subtrait of [`VcValueTrait`]. +/// +/// ```ignore +/// #[turbo_tasks::value_trait] +/// pub trait MyTrait { +/// +/// #[turbo_tasks::function] +/// fn method(self: Vc, a: i32) -> Vc; +/// +/// // External signature: fn method(self: Vc, a: i32) -> Vc +/// #[turbo_tasks::function] +/// async fn method2(&self, a: i32) -> Result> { +/// // Default implementation +/// } +/// +/// // A normal trait item, not a turbo-task +/// fn normal(&self) -> SomethingElse; +/// } +/// +/// #[turbo_tasks::value_trait] +/// pub trait OtherTrait: MyTrait + ValueToString { +/// // ... +/// } +/// +/// #[turbo_tasks::value_impl] +/// impl MyTrait for MyValue { +/// // only the external signature must match (see the docs for #[turbo_tasks::function]) +/// #[turbo_tasks::function] +/// fn method(&self, a: i32) -> Vc { +/// todo!() +/// } +/// +/// fn normal(&self) -> SomethingElse { +/// todo!() +/// } +/// } +/// ``` +/// +/// The `#[turbo_tasks::value_trait]` annotation derives [`VcValueTrait`] and registers the trait +/// and its methods. +/// +/// All methods annotated with [`#[turbo_tasks::function]`][function] are cached, and +/// the external signature rewriting rules defined on that macro are applied. +/// +/// Default implementation are supported. /// /// ## Arguments /// -/// Example: `#[turbo_tasks::value_trait(no_debug, resolved)]` +/// Example: `#[turbo_tasks::value_trait(no_debug, operation)]` /// -/// ### 'no_debug` +/// ### `no_debug` /// -/// Disables the automatic implementation of [`ValueDebug`][crate::debug::ValueDebug]. +/// Disables the automatic implementation of [`ValueDebug`][debug::ValueDebug]. /// /// Example: `#[turbo_tasks::value_trait(no_debug)]` /// -/// ### 'resolved` +/// ### `Operation` /// -/// Adds [`NonLocalValue`] as a supertrait of this trait. +/// Adds [`OperationValue`] as a supertrait of this trait. /// -/// Example: `#[turbo_tasks::value_trait(resolved)]` +/// Example: `#[turbo_tasks::value_trait(operation)]` #[rustfmt::skip] pub use turbo_tasks_macros::value_trait; +/// A macro used on any `impl` block for a [`VcValueType`]. This can either be an inherent +/// implementation or a trait implementation (see [`turbo_tasks::value_trait`][value_trait] and +/// [`VcValueTrait`]). +/// +/// Methods should be annotated with the [`#[turbo_tasks::function]`][function] macro. +/// +/// ```ignore +/// #[turbo_tasks::value_impl] +/// impl MyTrait for MyValue { +/// #[turbo_tasks::function] +/// fn method(&self, a: i32) -> Vc { +/// todo!() +/// } +/// } +/// ``` +#[rustfmt::skip] +pub use turbo_tasks_macros::value_impl; + /// Derives the TaskStorage struct and generates optimized storage structures. /// /// This macro analyzes `field` annotations and generates: diff --git a/turbopack/crates/turbo-tasks/src/trait_ref.rs b/turbopack/crates/turbo-tasks/src/trait_ref.rs index 1d921c0584183..0e90deacceb2f 100644 --- a/turbopack/crates/turbo-tasks/src/trait_ref.rs +++ b/turbopack/crates/turbo-tasks/src/trait_ref.rs @@ -9,12 +9,11 @@ use crate::{ vc::{ReadVcFuture, VcValueTraitCast, cast::VcCast}, }; -/// Similar to a [`ReadRef`][crate::ReadRef], but contains a value trait -/// object instead. +/// Similar to a [`ReadRef`][crate::ReadRef], but contains a value trait object instead. /// -/// The only way to interact with a `TraitRef` is by passing -/// it around or turning it back into a value trait vc by calling -/// [`ReadRef::cell`][crate::ReadRef::cell]. +/// Non-turbo-task methods with a `&self` receiver can be called on this reference. +/// +/// A `TraitRef` can be turned back into a value trait vc by calling [`TraitRef::cell`]. /// /// Internally it stores a reference counted reference to a value on the heap. pub struct TraitRef diff --git a/turbopack/crates/turbo-tasks/src/vc/traits.rs b/turbopack/crates/turbo-tasks/src/vc/traits.rs index c4c5cdd95b92c..8773f92333a81 100644 --- a/turbopack/crates/turbo-tasks/src/vc/traits.rs +++ b/turbopack/crates/turbo-tasks/src/vc/traits.rs @@ -3,15 +3,18 @@ use crate::{ vc::cell_mode::VcCellMode, }; -/// A trait implemented on all values types that can be put into a Value Cell -/// ([`Vc`][crate::Vc]). +/// A trait implemented on all values types that can be put into a value cell ([`Vc`][crate::Vc]). /// -/// # Safety +/// Custom traits for `VcValueType`s should be subtraits of [`VcValueTrait`]. +/// +/// You should not implement this trait directly, but instead use the +/// [`#[turbo_tasks::value]`][crate::value] macro instead. +/// +/// ## Safety /// -/// The implementor of this trait must ensure that the read and cell mode -/// implementations are correct for the value type. Otherwise, it is possible to -/// generate invalid reads, for instance by using -/// [`VcTransparentRead`][crate::VcTransparentRead] for a value type that is not +/// The implementor of this trait must ensure that the read and cell mode implementations are +/// correct for the value type. Otherwise, it is possible to generate invalid reads, for instance by +/// using [`VcTransparentRead`][crate::VcTransparentRead] for a value type that is not /// `#[repr(transparent)]`. pub unsafe trait VcValueType: ShrinkToFit + Sized + Send + Sync + 'static { /// How to read the value. @@ -26,8 +29,100 @@ pub unsafe trait VcValueType: ShrinkToFit + Sized + Send + Sync + 'static { fn has_serialization() -> bool; } -/// A trait implemented on all values trait object references that can be put -/// into a Value Cell ([`Vc>`][crate::Vc]). +/// A trait implemented on all values trait object references that can be used with a value cell +/// ([`Vc>`][crate::Vc]). +/// +/// You should not create subtraits of this trait manually, but instead use the +/// [`#[turbo_tasks::value_trait]`][crate::value_trait] macro. Implementations of `VcValueTrait`s +/// should use the [`#[turbo_tasks::value_impl]`][crate::value_impl] macro. +/// +/// [`Vc`]: crate::Vc +/// +/// +/// ## Upcasting +/// +/// A concrete [`Vc`] of a [`VcValueType`] can be converted to a [`Vc`] of a `VcValueTrait` with an +/// upcast: +/// +/// ```ignore +/// let something_vc: Vc = ...; +/// let trait_vc: Vc> = Vc::upcast(something_vc); +/// +/// // there is an equivalent API for ResolvedVc +/// let something_resolved_vc: ResolvedVc = ...; +/// let trait_resolved_vc: ResolvedVc> = ResolvedVc::upcast(something_resolved_vc); +/// ``` +/// +/// Upcast safety is enforced at compile-time with the [`Upcast`] and [`UpcastStrict`] traits. +/// Upcasts always succeed. +/// +/// +/// ## Downcasting +/// +/// A [`ResolvedVc`] containing a `VcValueTrait` subtrait can be downcast to a concrete type with +/// [`ResolvedVc::try_downcast_type`]: +/// +/// ```ignore +/// let trait_vc: Vc> = ...; +/// if let Some(something_vc) = ResolvedVc::try_downcast_type::(trait_vc) { +/// // ... +/// } +/// ``` +/// +/// A supertrait can be cast to a subtrait with [`ResolvedVc::try_downcast`]: +/// +/// ```ignore +/// let trait_vc: Vc> = ...; +/// if let Some(something_vc) = ResolvedVc::try_downcast::>(trait_vc) { +/// // ... +/// } +/// ``` +/// +/// If you have an unresolved [`Vc`] that you'd like to downcast, you should [resolve it first]. +/// +/// A compile-time check using the [`Upcast`] and [`UpcastStrict`] traits ensures that a downcast is +/// possible (the target type or trait implements the source trait), but it may still return +/// `None` at runtime if the concrete value does not implement the trait. +/// +/// [`ResolvedVc`]: crate::ResolvedVc +/// [`ResolvedVc::try_downcast_type`]: crate::ResolvedVc::try_downcast_type +/// [`ResolvedVc::try_downcast`]: crate::ResolvedVc::try_downcast +/// [resolve it first]: crate::Vc::to_resolved +/// +/// +/// ## Sidecasting +/// +/// In some cases, you may want to convert between two traits that do not have a supertrait/subtrait +/// relationship: +/// +/// ```ignore +/// let trait_vc: Vc> = ...; +/// if let Some(something_vc) = ResolvedVc::try_sidecast::>(trait_vc) { +/// // ... +/// } +/// ``` +/// +/// If you have an unresolved [`Vc`] that you'd like to sidecast, you should [resolve it first]. +/// +/// This won't do any compile-time checks, so downcasting should be preferred if possible. It will +/// return `None` at runtime if the cast fails. +/// +/// +/// ## Reading +/// +/// Trait object Vcs can be read by converting them to a [`TraitRef`], which allows non-turbo-tasks +/// functions defined on the trait to be called. +/// +/// ```ignore +/// use turbo_tasks::IntoTraitRef; +/// +/// let trait_vc: Vc> = ...; +/// let trait_ref: TraitRef> = trait_vc.into_trait_ref().await?; +/// +/// trait_ref.non_turbo_tasks_function(); +/// ``` +/// +/// [`TraitRef`]: crate::TraitRef pub trait VcValueTrait: NonLocalValue + Send + Sync + 'static { // The concrete type of the value_trait implementing VcValueTrait type ValueTrait: ?Sized; @@ -40,8 +135,10 @@ pub trait VcValueTrait: NonLocalValue + Send + Sync + 'static { fn get_impl_vtables() -> &'static VTableRegistry; } -/// Marker trait that indicates that a [`Vc`][crate::Vc] can be upcasted -/// to a [`Vc`][crate::Vc]. +/// Marker trait that indicates that a [`Vc`][crate::Vc] can be upcasted to a +/// [`Vc`][crate::Vc]. +/// +/// See [`VcValueTrait`] for example usage. /// /// # Safety /// @@ -53,7 +150,9 @@ where { } -/// A speialization of [`Upcast`] that ensures that the upcast is strict meaning that T !== Self +/// A specialization of [`Upcast`] that ensures that the upcast is strict meaning that `T !== Self`. +/// +/// See [`VcValueTrait`] for example usage. /// /// # Safety ///