diff --git a/turbopack/crates/turbo-tasks/README.md b/turbopack/crates/turbo-tasks/README.md index 66ea929406169..7a27625d1ecf2 100644 --- a/turbopack/crates/turbo-tasks/README.md +++ b/turbopack/crates/turbo-tasks/README.md @@ -11,13 +11,11 @@ Turbo Tasks defines 4 primitives: - **[Collectibles][crate::TurboTasks::emit_collectible]:** Values emitted in functions that bubble up the call graph and can be collected in parent functions. Collectibles are deduplicated by [cell id equality]. It defines some derived elements from that: -- **[Tasks][book-tasks]:** An instance of a function together with its arguments. +- **Tasks:** An instance of a function together with its arguments. - **[`Vc`s ("Value Cells")][`Vc`]:** References to locations associated with tasks where values are stored. The contents of a cell can change after the reexecution of a function due to invalidation. A [`Vc`] can be read to get [a read-only reference][crate::ReadRef] to the stored data, representing a snapshot of that cell at that point in time. [blog-post]: https://nextjs.org/blog/turbopack-incremental-computation [cell id equality]: crate::ResolvedVc#equality--hashing -[book-cells]: https://turbopack-rust-docs.vercel.sh/turbo-engine/cells.html -[book-tasks]: https://turbopack-rust-docs.vercel.sh/turbo-engine/tasks.html [`Vc`]: crate::Vc ## Functions and Tasks diff --git a/turbopack/crates/turbo-tasks/function.md b/turbopack/crates/turbo-tasks/function.md new file mode 100644 index 0000000000000..f96b22784418b --- /dev/null +++ b/turbopack/crates/turbo-tasks/function.md @@ -0,0 +1,140 @@ +Tasks are created by defining a Rust function annotated with the `#[turbo_tasks::function]` macro and calling it with arguments. Each unique combination of function and arguments create a new task at runtime. Tasks are the fundamental units of work within the build system. + +```rust +#[turbo_tasks::function] +fn add(a: i32, b: i32) -> Vc { + // Task implementation goes here... +} +``` + +- Tasks can be implemented as either a **synchronous or asynchronous** function. +- Arguments must implement the **[`TaskInput`] trait**. Usually these are primitives or types wrapped in [`Vc`]. +- The **external signature** of a task always **returns a [`Vc`]** or an [`OperationVc`]. +- **Generics** (type or lifetime parameters) are **not supported** in task functions. + +[`Vc`]: crate::Vc +[`TaskInput`]: crate::TaskInput + +## External Signature Rewriting + +The `#[turbo_tasks::function]` macro **rewrites the arguments and return values** of functions. The rewritten function signature is referred to as the **"external signature"**. + +### Argument Rewrite Rule + +- Function arguments with the **[`ResolvedVc`]** type are **rewritten to [`Vc`].** + - The value cell is automatically resolved when the function is called. This reduces the work needed to convert between `Vc` and `ResolvedVc` types. + - This rewrite applies for [`ResolvedVc`] types nested inside of `Option>` and `Vec>`. For more details, refer to the [`FromTaskInput`] trait. + +- Method arguments of **`&self`** are **rewritten to `self: Vc`**. + +[`ResolvedVc`]: crate::ResolvedVc +[`FromTaskInput`]: crate::FromTaskInput + +### Return Type Rewrite Rules + +- A return type of **`Result>` is rewritten into `Vc`**. + - The `Result>` return type allows for idiomatic use of the `?` operator inside of task functions. +- A function with **no return type** is rewritten to return **`Vc<()>` instead of `()`**. +- The **[`impl Future>`][Future]** type implicitly returned by an async function is **flattened into the `Vc` type**, which implements [`IntoFuture`] and can be `.await`ed. + +Some of this logic is represented by the [`TaskOutput`] trait and its associated [`Return`] type. + +[`TaskOutput`]: crate::task::TaskOutput +[`Return`]: crate::task::TaskOutput::Return + +### External Signature Example + +As an example, the method + +```rust +#[turbo_tasks::function] +async fn foo( + &self, + a: i32, + b: Vc, + c: ResolvedVc, + d: Option>>, +) -> Result> { + // ... +} +``` + +will have an external signature of + +```rust +fn foo( + self: Vc, // was: &self + a: i32, + b: Vc, + c: Vc, // was: ResolvedVc + d: Option>>, // was: Option>> +) -> Vc; // was: impl Future>> +``` + +## Methods and Self + +Tasks can be methods associated with a value or a trait implementation using the [`arbitrary_self_types` nightly compiler feature][self-types]. + +[self-types]: https://github.com/rust-lang/rfcs/blob/master/text/3519-arbitrary-self-types-v2.md + +### Inherent Implementations + +```rust +#[turbo_tasks::value_impl] +impl Something { + #[turbo_tasks::function] + fn method(self: Vc, a: i32) -> Vc { + // Receives the full `Vc` type, which we must `.await` to get a + // `ReadRef`. + vdbg!(self.await?.some_field); + + // The `Vc` type is useful for calling other methods declared on + // `Vc`, e.g.: + self.method_resolved(a) + } + + #[turbo_tasks::function] + fn method_resolved(self: ResolvedVc, a: i32) -> Vc { + // Same as above, but receives a `ResolvedVc`, which can be `.await`ed + // to a `ReadRef` or dereferenced (implicitly or with `*`) to `Vc`. + vdbg!(self.await?.some_field); + + // The `ResolvedVc` type can be used to call other methods + // declared on `Vc`, e.g.: + self.method_ref(a) + } + + #[turbo_tasks::function] + fn method_ref(&self, a: i32) -> Vc { + // As a convenience, receives the fully resolved version of `self`. This + // does not require `.await`ing to read. + // + // It can access fields on the struct/enum and call methods declared on + // `Self`, but it cannot call other methods declared on `Vc` + // (without cloning the value and re-wrapping it in a `Vc`). + Vc::cell(SomethingElse::new(self.some_field, a)) + } +} +``` + +- **Declaration Location:** The methods are defined on [`Vc`] (i.e. `Vc::::method` and `Vc::::method2`), not on the inner type. + +- **`&self` Syntactic Sugar:** The `&self` argument of a `#[turbo_tasks::function]` implicitly reads the value from `self: Vc`. + +- **External Signature Rewriting:** All of the signature rewrite rules apply here. `self` can be [`ResolvedVc`]. `async` and `Result>` return types are supported. + +### Trait Implementations + +```rust +#[turbo_tasks::value_impl] +impl Trait for Something { + #[turbo_tasks::function] + fn method(self: Vc, a: i32) -> Vc { + // Trait method implementation... + // + // `self: ResolvedVc` and `&self` are also valid argument types! + } +} +``` + +For traits, only the external signature (after rewriting) must align with the trait definition. diff --git a/turbopack/crates/turbo-tasks/src/lib.rs b/turbopack/crates/turbo-tasks/src/lib.rs index 27894e8e6c4fe..907ae526f08f1 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::{TaskInput, function, turbobail, turbofmt, value_impl}; +pub use turbo_tasks_macros::{TaskInput, turbobail, turbofmt, value_impl}; pub use crate::{ capture_future::TurboTasksPanic, @@ -159,6 +159,10 @@ macro_rules! fxindexset { }; } +#[doc = include_str!("../function.md")] +#[rustfmt::skip] +pub use turbo_tasks_macros::function; + /// Implements [`VcValueType`] for the given `struct` or `enum`. These value types can be used /// inside of a "value cell" as [`Vc<...>`][Vc]. ///