Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Generic types over Vcs (vercel/turborepo#5738)
### Description This PR enables types generic over `Vc`s to work as value types. In other words, where we would previously need to declare all generic/container types over a given value type at the definition site, like the following: ```rust #[turbo_tasks::value] struct Thing; #[turbo_tasks::value(transparent)] struct Things(Vec<Vc<Thing>>); #[turbo_tasks::value(transparent)] struct ThingOption(Option<Vc<Thing>>); #[turbo_tasks::value(transparent)] struct ThingSet(IndexSet<Vc<Thing>>); #[turbo_tasks::value(transparent)] struct ThingMap(IndexMap<Vc<ThingKey>, Vc<Thing>>); let vec: Vc<Things> = Vc::cell(vec![Thing.cell()]); let option: Vc<ThingOption> = Vc::cell(Some(Thing.cell())); // etc. ``` We can now refer to `Vc<Vec<Vc<Thing>>>` directly, without needing to declare an intermediate transparent value type: ```rust #[turbo_tasks::value] struct Thing; let vec: Vc<Vec<_>> = Vc::cell(vec![Thing.cell()]); let option: Vc<Option<_>> = Vc::cell(Some(Thing.cell())); // etc. ``` The following generic types and methods are currently supported: * `Option<Vc<T>>` * `::is_some(self: Vc<Self>) -> Vc<bool>` * `::is_none(self: Vc<Self>) -> Vc<bool>` * `Vec<Vc<T>>` * `::is_empty(self: Vc<Self>) -> Vc<bool>` * `::len(self: Vc<Self>) -> Vc<usize>` * `IndexSet<Vc<T>>` * `::is_empty(self: Vc<Self>) -> Vc<bool>` * `::len(self: Vc<Self>) -> Vc<usize>` * `IndexMap<Vc<K>, Vc<V>>` * `::is_empty(self: Vc<Self>) -> Vc<bool>` * `::len(self: Vc<Self>) -> Vc<usize>` All these value types also implement `ValueDefault`, which means that you can instantiate them using `Default::default()` or `Vc::default()`: ```rust let vec: Vc<Vec<Vc<u32>>> = Default::default() ``` They also work recursively, but I would recommend creating intermediate types instead of this in most (all?) cases: ```rust let vec: Vc<Vec<Vc<Vec<Vc<u32>>>>> = Default::default() ``` ### Notes and implementation details 1. This only works when the generic type is wrapped in a `Vc`. In the example above, this would not cover `Vc<Vec<Thing>>` directly. The implementation relies on the fact that all `Type<Vc<T>>` are represented the exact same way in memory, and as such can all be cast to some common `Type<Vc<()>>` for storage. 2. Like primitives, these generic types can currently only be declared in `turbo_tasks` itself. Technically, if a crate owns a type `A`, it could also register `A` as a generic type, so we might want to allow this in the future (e.g. `AliasMap` in `turbopack`?) 3. There's some potential performance/correctness issues around `cell`ing, I'll defer to @sokra to figure out if this approach is worth it. ### Testing Instructions I added some tests to `all_in_one.rs`. --------- Co-authored-by: Justin Ridgewell <justin@ridgewell.name> Co-authored-by: Tobias Koppers <tobias.koppers@googlemail.com>
- Loading branch information