diff --git a/Cargo.lock b/Cargo.lock index dff4502909952..823597585b7e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10261,6 +10261,7 @@ name = "turbo-tasks-macros" version = "0.1.0" dependencies = [ "anyhow", + "either", "proc-macro-error", "proc-macro2", "quote", diff --git a/crates/turbo-tasks-macros-tests/tests/derive_resolved_value.rs b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value.rs new file mode 100644 index 0000000000000..401162c105fb9 --- /dev/null +++ b/crates/turbo-tasks-macros-tests/tests/derive_resolved_value.rs @@ -0,0 +1,17 @@ +use turbo_tasks::{ResolvedValue, ResolvedVc, Vc}; + +#[derive(ResolvedValue)] +struct ContainsResolvedVc { + a: ResolvedVc, +} + +#[derive(ResolvedValue)] +struct ContainsVc { + a: Vc, +} + +#[derive(ResolvedValue)] +struct ContainsMixedVc { + a: Vc, + b: ResolvedVc, +} diff --git a/crates/turbo-tasks-macros/Cargo.toml b/crates/turbo-tasks-macros/Cargo.toml index 9d6509310a716..0200335e03ce2 100644 --- a/crates/turbo-tasks-macros/Cargo.toml +++ b/crates/turbo-tasks-macros/Cargo.toml @@ -14,6 +14,7 @@ workspace = true [dependencies] anyhow = { workspace = true } +either = { workspace = true } proc-macro-error = "1.0.4" proc-macro2 = { workspace = true } quote = { workspace = true } diff --git a/crates/turbo-tasks-macros/src/derive/mod.rs b/crates/turbo-tasks-macros/src/derive/mod.rs index 953fc350c8e02..d48323309096e 100644 --- a/crates/turbo-tasks-macros/src/derive/mod.rs +++ b/crates/turbo-tasks-macros/src/derive/mod.rs @@ -1,10 +1,12 @@ mod deterministic_hash_macro; +mod resolved_value_macro; mod task_input_macro; mod trace_raw_vcs_macro; mod value_debug_format_macro; mod value_debug_macro; pub use deterministic_hash_macro::derive_deterministic_hash; +pub use resolved_value_macro::derive_resolved_value; use syn::{spanned::Spanned, Attribute, Meta, MetaList, NestedMeta}; pub use task_input_macro::derive_task_input; pub use trace_raw_vcs_macro::derive_trace_raw_vcs; diff --git a/crates/turbo-tasks-macros/src/derive/resolved_value_macro.rs b/crates/turbo-tasks-macros/src/derive/resolved_value_macro.rs new file mode 100644 index 0000000000000..0b44a48afd025 --- /dev/null +++ b/crates/turbo-tasks-macros/src/derive/resolved_value_macro.rs @@ -0,0 +1,50 @@ +use either::Either; +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::{quote, quote_spanned}; +use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput, Generics, Type}; + +pub fn derive_resolved_value(input: TokenStream) -> TokenStream { + let derive_input = parse_macro_input!(input as DeriveInput); + let ident = &derive_input.ident; + + let assertions: Vec<_> = iter_data_fields(&derive_input.data) + .map(|f| assert_field_resolved_value(&derive_input.generics, &f.ty)) + .collect(); + + let (impl_generics, ty_generics, where_clause) = derive_input.generics.split_for_impl(); + quote! { + unsafe impl #impl_generics ::turbo_tasks::ResolvedValue + for #ident #ty_generics #where_clause {} + #(#assertions)* + } + .into() +} + +fn iter_data_fields(data: &Data) -> impl Iterator { + match data { + Data::Struct(ds) => Either::Left(ds.fields.iter()), + Data::Enum(de) => Either::Right(Either::Left(de.variants.iter().flat_map(|v| &v.fields))), + Data::Union(du) => Either::Right(Either::Right(du.fields.named.iter())), + } +} + +fn assert_field_resolved_value(generics: &Generics, ty: &Type) -> TokenStream2 { + // this technique is based on the trick used by + // `static_assertions::assert_impl_all`, but extended to support generics. + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + quote_spanned! { + ty.span() => + const _: fn() = || { + // create this struct just to hold onto our generics + struct StaticAssertion #impl_generics #where_clause; + impl #impl_generics StaticAssertion #ty_generics #where_clause { + fn assert_impl_resolved_value() {} + fn call_site() { + // this call is only valid if ty is a ResolvedValue + Self::assert_impl_resolved_value::<#ty>(); + } + } + }; + } +} diff --git a/crates/turbo-tasks-macros/src/lib.rs b/crates/turbo-tasks-macros/src/lib.rs index f9977e5afd1c4..c1ced476b8e28 100644 --- a/crates/turbo-tasks-macros/src/lib.rs +++ b/crates/turbo-tasks-macros/src/lib.rs @@ -22,6 +22,11 @@ pub fn derive_trace_raw_vcs_attr(input: TokenStream) -> TokenStream { derive::derive_trace_raw_vcs(input) } +#[proc_macro_derive(ResolvedValue, attributes(turbo_tasks))] +pub fn derive_resolved_value_attr(input: TokenStream) -> TokenStream { + derive::derive_resolved_value(input) +} + #[proc_macro_derive(ValueDebug, attributes(turbo_tasks))] pub fn derive_value_debug_attr(input: TokenStream) -> TokenStream { derive::derive_value_debug(input) diff --git a/crates/turbo-tasks/src/vc/resolved.rs b/crates/turbo-tasks/src/vc/resolved.rs index 7b3a5a6bd666d..a9c7cad14ca31 100644 --- a/crates/turbo-tasks/src/vc/resolved.rs +++ b/crates/turbo-tasks/src/vc/resolved.rs @@ -121,3 +121,5 @@ unsafe impl ResolvedValue for Arc {} unsafe impl ResolvedValue for Result {} unsafe impl ResolvedValue for Mutex {} unsafe impl ResolvedValue for RefCell {} + +pub use turbo_tasks_macros::ResolvedValue;