diff --git a/crates/turbo-tasks-build/src/lib.rs b/crates/turbo-tasks-build/src/lib.rs index b44820d34e628..266797b3a6130 100644 --- a/crates/turbo-tasks-build/src/lib.rs +++ b/crates/turbo-tasks-build/src/lib.rs @@ -18,7 +18,7 @@ use turbo_tasks_macros_shared::{ get_impl_function_ident, get_native_function_ident, get_path_ident, get_register_trait_methods_ident, get_register_value_type_ident, get_trait_default_impl_function_ident, get_trait_impl_function_ident, get_trait_type_ident, - get_type_ident, GenericTypeInput, PrimitiveInput, ValueTraitArguments, + get_type_ident, GenericTypeInput, PrimitiveInput, }; pub fn generate_register() { @@ -361,10 +361,10 @@ impl<'a> RegisterContext<'a> { } fn process_trait_inner(&mut self, trait_item: &ItemTrait) -> Result<()> { - if let Some(attr) = trait_item + if trait_item .attrs .iter() - .find(|a| is_turbo_attribute(a, "value_trait")) + .any(|a| is_turbo_attribute(a, "value_trait")) { let trait_ident = &trait_item.ident; @@ -388,16 +388,6 @@ impl<'a> RegisterContext<'a> { let trait_type_ident = get_trait_type_ident(trait_ident); self.register(trait_type_ident, self.get_global_name(&[trait_ident]))?; - - let trait_args: ValueTraitArguments = parse_attr_args(attr)?.unwrap_or_default(); - if trait_args.debug { - self.register_debug_impl( - &get_type_ident(&parse_quote! { - Box - }) - .unwrap(), - )?; - } } Ok(()) } @@ -610,14 +600,3 @@ fn is_cfg_attribute(attr: &Attribute) -> bool { .get_ident() .is_some_and(|ident| ident == "cfg" || ident == "cfg_attr") } - -fn parse_attr_args(attr: &Attribute) -> syn::Result> -where - T: syn::parse::Parse, -{ - if attr.tokens.is_empty() { - Ok(None) - } else { - Ok(Some(attr.parse_args_with(T::parse)?)) - } -} diff --git a/crates/turbo-tasks-macros-shared/src/expand.rs b/crates/turbo-tasks-macros-shared/src/expand.rs index 7f27c42130f9d..dfcb4108a8255 100644 --- a/crates/turbo-tasks-macros-shared/src/expand.rs +++ b/crates/turbo-tasks-macros-shared/src/expand.rs @@ -20,9 +20,9 @@ use syn::{ /// These helpers should themselves call [generate_destructuring] to generate /// the destructure necessary to access the fields of the value. pub fn match_expansion< - EN: Fn(&Ident, &FieldsNamed) -> (TokenStream, TokenStream), - EU: Fn(&Ident, &FieldsUnnamed) -> (TokenStream, TokenStream), - U: Fn(&Ident) -> TokenStream, + EN: Fn(TokenStream, &FieldsNamed) -> (TokenStream, TokenStream), + EU: Fn(TokenStream, &FieldsUnnamed) -> (TokenStream, TokenStream), + U: Fn(TokenStream) -> TokenStream, >( derive_input: &DeriveInput, expand_named: &EN, @@ -33,36 +33,48 @@ pub fn match_expansion< let expand_unit = move |ident| (TokenStream::new(), expand_unit(ident)); match &derive_input.data { Data::Enum(DataEnum { variants, .. }) => { - let (variants_idents, (variants_fields_capture, expansion)): ( - Vec<_>, - (Vec<_>, Vec<_>), - ) = variants - .iter() - .map(|variant| { - ( - &variant.ident, - expand_fields( - &variant.ident, - &variant.fields, - expand_named, - expand_unnamed, - expand_unit, - ), - ) - }) - .unzip(); + let (idents, (variants_fields_capture, expansion)): (Vec<_>, (Vec<_>, Vec<_>)) = + variants + .iter() + .map(|variant| { + let variants_idents = &variant.ident; + let ident = quote! { #ident::#variants_idents }; + ( + ident.clone(), + expand_fields( + ident, + &variant.fields, + expand_named, + expand_unnamed, + expand_unit, + ), + ) + }) + .unzip(); - quote! { - match self { - #( - #ident::#variants_idents #variants_fields_capture => #expansion, - )* + if idents.is_empty() { + let (_, expansion) = expand_unit(quote! { #ident }); + quote! { + #expansion + } + } else { + quote! { + match self { + #( + #idents #variants_fields_capture => #expansion, + )* + } } } } Data::Struct(DataStruct { fields, .. }) => { - let (captures, expansion) = - expand_fields(ident, fields, expand_named, expand_unnamed, expand_unit); + let (captures, expansion) = expand_fields( + quote! { #ident }, + fields, + expand_named, + expand_unnamed, + expand_unit, + ); if fields.is_empty() { assert!(captures.is_empty()); @@ -100,12 +112,12 @@ pub fn match_expansion< pub fn expand_fields< 'ident, 'fields, - EN: Fn(&'ident Ident, &'fields FieldsNamed) -> R, - EU: Fn(&'ident Ident, &'fields FieldsUnnamed) -> R, - U: Fn(&'ident Ident) -> R, + EN: Fn(TokenStream, &'fields FieldsNamed) -> R, + EU: Fn(TokenStream, &'fields FieldsUnnamed) -> R, + U: Fn(TokenStream) -> R, R, >( - ident: &'ident Ident, + ident: TokenStream, fields: &'fields Fields, expand_named: EN, expand_unnamed: EU, diff --git a/crates/turbo-tasks-macros-tests/Cargo.toml b/crates/turbo-tasks-macros-tests/Cargo.toml index 5080fc64bf1b6..cf8988291657c 100644 --- a/crates/turbo-tasks-macros-tests/Cargo.toml +++ b/crates/turbo-tasks-macros-tests/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dev-dependencies] anyhow = { workspace = true } +serde = { workspace = true } tokio = { workspace = true } turbo-tasks = { workspace = true } turbo-tasks-memory = { workspace = true } diff --git a/crates/turbo-tasks-macros-tests/tests/task_input.rs b/crates/turbo-tasks-macros-tests/tests/task_input.rs index 7eabe06e52247..fa516a8180ae5 100644 --- a/crates/turbo-tasks-macros-tests/tests/task_input.rs +++ b/crates/turbo-tasks-macros-tests/tests/task_input.rs @@ -2,12 +2,13 @@ //! However, we keep one test here as an integration test between the derive //! macro and the `#[turbo_tasks::function]` macro. +use serde::{Deserialize, Serialize}; use turbo_tasks::{Completion, TaskInput, Vc}; use turbo_tasks_testing::{register, run}; register!(); -#[derive(Clone, TaskInput)] +#[derive(Clone, TaskInput, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] struct OneUnnamedField(u32); #[turbo_tasks::function] diff --git a/crates/turbo-tasks-macros/src/derive/deterministic_hash_macro.rs b/crates/turbo-tasks-macros/src/derive/deterministic_hash_macro.rs index 8e92839505555..33774a1c90602 100644 --- a/crates/turbo-tasks-macros/src/derive/deterministic_hash_macro.rs +++ b/crates/turbo-tasks-macros/src/derive/deterministic_hash_macro.rs @@ -1,5 +1,5 @@ use proc_macro::TokenStream; -use proc_macro2::{Ident, TokenStream as TokenStream2}; +use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::{parse_macro_input, Data, DeriveInput, FieldsNamed, FieldsUnnamed}; use turbo_tasks_macros_shared::{generate_exhaustive_destructuring, match_expansion}; @@ -35,7 +35,7 @@ pub fn derive_deterministic_hash(input: TokenStream) -> TokenStream { /// Hashes a struct or enum variant with named fields (e.g. `struct Foo { /// bar: u32 }`, `Foo::Bar { baz: u32 }`). -fn hash_named(_ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStream2) { +fn hash_named(_ident: TokenStream2, fields: &FieldsNamed) -> (TokenStream2, TokenStream2) { let (captures, fields_idents) = generate_exhaustive_destructuring(fields.named.iter()); ( captures, @@ -49,7 +49,7 @@ fn hash_named(_ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStrea /// Hashes a struct or enum variant with unnamed fields (e.g. `struct /// Foo(u32)`, `Foo::Bar(u32)`). -fn hash_unnamed(_ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, TokenStream2) { +fn hash_unnamed(_ident: TokenStream2, fields: &FieldsUnnamed) -> (TokenStream2, TokenStream2) { let (captures, fields_idents) = generate_exhaustive_destructuring(fields.unnamed.iter()); ( captures, @@ -62,6 +62,6 @@ fn hash_unnamed(_ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, TokenS } /// Hashes a unit struct or enum variant (e.g. `struct Foo;`, `Foo::Bar`). -fn hash_unit(_ident: &Ident) -> TokenStream2 { +fn hash_unit(_ident: TokenStream2) -> TokenStream2 { quote! { { } } } diff --git a/crates/turbo-tasks-macros/src/derive/task_input_macro.rs b/crates/turbo-tasks-macros/src/derive/task_input_macro.rs index a6c0169096c66..7a96916784d59 100644 --- a/crates/turbo-tasks-macros/src/derive/task_input_macro.rs +++ b/crates/turbo-tasks-macros/src/derive/task_input_macro.rs @@ -1,11 +1,7 @@ use proc_macro::TokenStream; -use proc_macro2::{Ident, Literal, TokenStream as TokenStream2}; use quote::quote; -use syn::{ - parse_macro_input, spanned::Spanned, Data, DataEnum, DataStruct, DeriveInput, FieldsNamed, - FieldsUnnamed, -}; -use turbo_tasks_macros_shared::{expand_fields, generate_exhaustive_destructuring}; +use syn::{parse_macro_input, spanned::Spanned, DeriveInput}; +use turbo_tasks_macros_shared::{generate_exhaustive_destructuring, match_expansion}; pub fn derive_task_input(input: TokenStream) -> TokenStream { let derive_input = parse_macro_input!(input as DeriveInput); @@ -55,127 +51,90 @@ pub fn derive_task_input(input: TokenStream) -> TokenStream { } } - let inputs_list_ident = Ident::new( - &format!("__{}_inputs_list", ident), - derive_input.ident.span(), + let is_resolved_impl = match_expansion( + &derive_input, + &|_ident, fields| { + let (capture, fields) = generate_exhaustive_destructuring(fields.named.iter()); + ( + capture, + quote! { + {#( + #fields.is_resolved() && + )* true} + }, + ) + }, + &|_ident, fields| { + let (capture, fields) = generate_exhaustive_destructuring(fields.unnamed.iter()); + ( + capture, + quote! { + {#( + #fields.is_resolved() && + )* true} + }, + ) + }, + &|_ident| quote! {true}, ); - - let expand_named = |ident, fields| expand_named(ident, fields, &inputs_list_ident); - let expand_unnamed = |ident, fields| expand_unnamed(ident, fields, &inputs_list_ident); - - let (try_from_impl, from_impl) = match &derive_input.data { - Data::Enum(DataEnum { variants, .. }) => { - let mut variants_idents = vec![]; - let mut variants_fields_len = vec![]; - let mut variants_fields_destructuring = vec![]; - let mut variants_try_from_expansion = vec![]; - let mut variants_from_expansion = vec![]; - - for variant in variants { - let variant_ident = &variant.ident; - let (fields_destructuring, try_from_expansion, from_expansion) = expand_fields( - &variant.ident, - &variant.fields, - expand_named, - expand_unnamed, - expand_unit, - ); - variants_idents.push(variant_ident); - variants_fields_len.push(variant.fields.len()); - variants_fields_destructuring.push(fields_destructuring); - variants_try_from_expansion.push(try_from_expansion); - variants_from_expansion.push(from_expansion); - } - - // This is similar to what Rust does for enums (configurable via the `repr` - // attribute). We use the smallest possible integer type that can - // represent all the variants. However, for now, this is not - // configurable for TaskInput enums. - let repr_bits = usize::BITS - variants.len().leading_zeros(); - let repr = match repr_bits { - 0..=8 => quote! { u8 }, - 9..=16 => quote! { u16 }, - 17..=32 => quote! { u32 }, - 33..=64 => quote! { u64 }, - _ => panic!("too many variants"), - }; - - let variants_discriminants: Vec<_> = (0..variants_idents.len()) - .map(Literal::usize_unsuffixed) - .collect(); - + let is_transient_impl = match_expansion( + &derive_input, + &|_ident, fields| { + let (capture, fields) = generate_exhaustive_destructuring(fields.named.iter()); ( + capture, quote! { - match value { - turbo_tasks::ConcreteTaskInput::List(value) => { - let mut #inputs_list_ident = value.iter(); - - let discriminant = #inputs_list_ident.next().ok_or_else(|| anyhow::anyhow!(concat!("missing discriminant for ", stringify!(#ident))))?; - let discriminant: #repr = turbo_tasks::TaskInput::try_from_concrete(discriminant)?; - - Ok(match discriminant { - #( - #variants_discriminants => { - #variants_try_from_expansion - #ident::#variants_idents #variants_fields_destructuring - }, - )* - _ => return Err(anyhow::anyhow!("invalid discriminant for {}", stringify!(#ident))), - }) - }, - _ => Err(anyhow::anyhow!("invalid task input type, expected list (enum)")), - } + {#( + #fields.is_transient() || + )* false} }, + ) + }, + &|_ident, fields| { + let (capture, fields) = generate_exhaustive_destructuring(fields.unnamed.iter()); + ( + capture, quote! { - match self { + {#( + #fields.is_transient() || + )* false} + }, + ) + }, + &|_ident| quote! {false}, + ); + let resolve_impl = match_expansion( + &derive_input, + &|ident, fields| { + let (capture, fields) = generate_exhaustive_destructuring(fields.named.iter()); + ( + capture, + quote! { + { #( - #ident::#variants_idents #variants_fields_destructuring => { - let mut #inputs_list_ident = Vec::with_capacity(1 + #variants_fields_len); - let discriminant: #repr = #variants_discriminants; - let discriminant = discriminant.into_concrete(); - #inputs_list_ident.push(discriminant); - #variants_from_expansion - turbo_tasks::ConcreteTaskInput::List(#inputs_list_ident) - } + let #fields = #fields.resolve().await?; )* + Ok(#ident { #(#fields),* }) } }, ) - } - Data::Struct(DataStruct { fields, .. }) => { - let (destructuring, try_from_expansion, from_expansion) = - expand_fields(ident, fields, expand_named, expand_unnamed, expand_unit); - let fields_len = fields.len(); - + }, + &|ident, fields| { + let (capture, fields) = generate_exhaustive_destructuring(fields.unnamed.iter()); ( + capture, quote! { - match value { - turbo_tasks::ConcreteTaskInput::List(value) => { - let mut #inputs_list_ident = value.iter(); - #try_from_expansion - Ok(#ident #destructuring) - }, - _ => Err(anyhow::anyhow!("invalid task input type, expected list (struct)")), + { + #( + let #fields = #fields.resolve().await?; + )* + Ok(#ident(#(#fields),*)) } }, - quote! { - let mut #inputs_list_ident = Vec::with_capacity(#fields_len); - let #ident #destructuring = self; - #from_expansion - turbo_tasks::ConcreteTaskInput::List(#inputs_list_ident) - }, ) - } - _ => { - derive_input - .span() - .unwrap() - .error("unsupported syntax") - .emit(); - - (quote! {}, quote! {}) - } - }; + }, + &|ident| quote! {Ok(#ident)}, + ); let generic_params: Vec<_> = generics .params @@ -190,72 +149,29 @@ pub fn derive_task_input(input: TokenStream) -> TokenStream { .collect(); quote! { + #[turbo_tasks::macro_helpers::async_trait] impl #generics turbo_tasks::TaskInput for #ident #generics where #(#generic_params: turbo_tasks::TaskInput,)* { #[allow(non_snake_case)] #[allow(unreachable_code)] // This can occur for enums with no variants. - fn try_from_concrete(value: &turbo_tasks::ConcreteTaskInput) -> turbo_tasks::Result { - #try_from_impl + fn is_resolved(&self) -> bool { + #is_resolved_impl } #[allow(non_snake_case)] #[allow(unreachable_code)] // This can occur for enums with no variants. - fn into_concrete(self) -> turbo_tasks::ConcreteTaskInput { - #from_impl + fn is_transient(&self) -> bool { + #is_transient_impl + } + + #[allow(non_snake_case)] + #[allow(unreachable_code)] // This can occur for enums with no variants. + async fn resolve(&self) -> turbo_tasks::Result { + #resolve_impl } } } .into() } - -fn expand_named( - _ident: &Ident, - fields: &FieldsNamed, - inputs_list_ident: &Ident, -) -> (TokenStream2, TokenStream2, TokenStream2) { - let (destructuring, fields_idents) = generate_exhaustive_destructuring(fields.named.iter()); - ( - destructuring, - quote! { - #( - let #fields_idents = #inputs_list_ident.next().ok_or_else(|| anyhow::anyhow!(concat!("missing element for ", stringify!(#fields_idents))))?; - let #fields_idents = turbo_tasks::TaskInput::try_from_concrete(#fields_idents)?; - )* - }, - quote! { - #( - let #fields_idents = #fields_idents.into_concrete(); - #inputs_list_ident.push(#fields_idents); - )* - }, - ) -} - -fn expand_unnamed( - _ident: &Ident, - fields: &FieldsUnnamed, - inputs_list_ident: &Ident, -) -> (TokenStream2, TokenStream2, TokenStream2) { - let (destructuring, fields_idents) = generate_exhaustive_destructuring(fields.unnamed.iter()); - ( - destructuring, - quote! { - #( - let #fields_idents = #inputs_list_ident.next().ok_or_else(|| anyhow::anyhow!(concat!("missing element for ", stringify!(#fields_idents))))?; - let #fields_idents = turbo_tasks::TaskInput::try_from_concrete(#fields_idents)?; - )* - }, - quote! { - #( - let #fields_idents = #fields_idents.into_concrete(); - #inputs_list_ident.push(#fields_idents); - )* - }, - ) -} - -fn expand_unit(_ident: &Ident) -> (TokenStream2, TokenStream2, TokenStream2) { - (quote! {}, quote! {}, quote! {}) -} diff --git a/crates/turbo-tasks-macros/src/derive/trace_raw_vcs_macro.rs b/crates/turbo-tasks-macros/src/derive/trace_raw_vcs_macro.rs index 95e38814eacde..f735624997d2b 100644 --- a/crates/turbo-tasks-macros/src/derive/trace_raw_vcs_macro.rs +++ b/crates/turbo-tasks-macros/src/derive/trace_raw_vcs_macro.rs @@ -1,5 +1,5 @@ use proc_macro::TokenStream; -use proc_macro2::{Ident, TokenStream as TokenStream2}; +use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::{parse_macro_input, DeriveInput, Field, FieldsNamed, FieldsUnnamed}; use turbo_tasks_macros_shared::{generate_destructuring, match_expansion}; @@ -32,7 +32,7 @@ pub fn derive_trace_raw_vcs(input: TokenStream) -> TokenStream { .into() } -fn trace_named(_ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStream2) { +fn trace_named(_ident: TokenStream2, fields: &FieldsNamed) -> (TokenStream2, TokenStream2) { let (captures, fields_idents) = generate_destructuring(fields.named.iter(), &filter_field); ( captures, @@ -44,7 +44,7 @@ fn trace_named(_ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStre ) } -fn trace_unnamed(_ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, TokenStream2) { +fn trace_unnamed(_ident: TokenStream2, fields: &FieldsUnnamed) -> (TokenStream2, TokenStream2) { let (captures, fields_idents) = generate_destructuring(fields.unnamed.iter(), &filter_field); ( captures, @@ -56,6 +56,6 @@ fn trace_unnamed(_ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, Token ) } -fn trace_unit(_ident: &Ident) -> TokenStream2 { +fn trace_unit(_ident: TokenStream2) -> TokenStream2 { quote! { { } } } diff --git a/crates/turbo-tasks-macros/src/derive/value_debug_format_macro.rs b/crates/turbo-tasks-macros/src/derive/value_debug_format_macro.rs index 52d6fa1e52961..5f721893c5f19 100644 --- a/crates/turbo-tasks-macros/src/derive/value_debug_format_macro.rs +++ b/crates/turbo-tasks-macros/src/derive/value_debug_format_macro.rs @@ -1,5 +1,5 @@ use proc_macro::TokenStream; -use proc_macro2::{Ident, TokenStream as TokenStream2}; +use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::{parse_macro_input, DeriveInput, Field, FieldsNamed, FieldsUnnamed}; use turbo_tasks_macros_shared::{generate_destructuring, match_expansion}; @@ -62,7 +62,7 @@ fn format_field(value: TokenStream2) -> TokenStream2 { /// Formats a struct or enum variant with named fields (e.g. `struct Foo { /// bar: u32 }`, `Foo::Bar { baz: u32 }`). -fn format_named(ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStream2) { +fn format_named(ident: TokenStream2, fields: &FieldsNamed) -> (TokenStream2, TokenStream2) { let (captures, fields_idents) = generate_destructuring(fields.named.iter(), &filter_field); ( captures, @@ -70,13 +70,13 @@ fn format_named(ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStre // this can happen if all fields are ignored, we must special-case this to avoid // rustc being unable to infer the type of an empty vec of futures quote! { - FormattingStruct::new_named(stringify!(#ident), vec![]) + FormattingStruct::new_named(turbo_tasks::stringify_path!(#ident), vec![]) } } else { let fields_values = fields_idents.iter().cloned().map(format_field); quote! { FormattingStruct::new_named_async( - stringify!(#ident), + turbo_tasks::stringify_path!(#ident), vec![#( AsyncFormattingField::new( stringify!(#fields_idents), @@ -91,7 +91,7 @@ fn format_named(ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStre /// Formats a struct or enum variant with unnamed fields (e.g. `struct /// Foo(u32)`, `Foo::Bar(u32)`). -fn format_unnamed(ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, TokenStream2) { +fn format_unnamed(ident: TokenStream2, fields: &FieldsUnnamed) -> (TokenStream2, TokenStream2) { let (captures, fields_idents) = generate_destructuring(fields.unnamed.iter(), &filter_field); ( captures, @@ -99,13 +99,13 @@ fn format_unnamed(ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, Token // this can happen if all fields are ignored, we must special-case this to avoid // rustc being unable to infer the type of an empty vec of futures quote! { - FormattingStruct::new_unnamed(stringify!(#ident), vec![]) + FormattingStruct::new_unnamed(turbo_tasks::stringify_path!(#ident), vec![]) } } else { let fields_values = fields_idents.into_iter().map(format_field); quote! { FormattingStruct::new_unnamed_async( - stringify!(#ident), + turbo_tasks::stringify_path!(#ident), vec![#( #fields_values, )*], @@ -116,10 +116,10 @@ fn format_unnamed(ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, Token } /// Formats a unit struct or enum variant (e.g. `struct Foo;`, `Foo::Bar`). -fn format_unit(ident: &Ident) -> TokenStream2 { +fn format_unit(ident: TokenStream2) -> TokenStream2 { quote! { FormattingStruct::new_unnamed( - stringify!(#ident), + turbo_tasks::stringify_path!(#ident), vec![], ) } diff --git a/crates/turbo-tasks-macros/src/func.rs b/crates/turbo-tasks-macros/src/func.rs index 6c8129cd634b4..d6840ded4fe59 100644 --- a/crates/turbo-tasks-macros/src/func.rs +++ b/crates/turbo-tasks-macros/src/func.rs @@ -174,8 +174,6 @@ impl TurboFn { // `turbo_tasks::Vc` here. // We'll rely on the compiler to emit an error // if the user provided an invalid receiver type - // when - // calling `into_concrete`. let ident = ident.ident.clone(); @@ -293,17 +291,17 @@ impl TurboFn { } } - fn converted_inputs(&self) -> Punctuated { + fn inputs(&self) -> Vec<&Ident> { self.inputs .iter() - .map(|Input { ty, ident }| -> Expr { - parse_quote! { - <#ty as turbo_tasks::task::TaskInput>::into_concrete(#ident) - } - }) + .map(|Input { ident, .. }| ident) .collect() } + pub fn input_types(&self) -> Vec<&Type> { + self.inputs.iter().map(|Input { ty, .. }| ty).collect() + } + fn converted_this(&self) -> Option { self.this.as_ref().map(|Input { ty: _, ident }| { parse_quote! { @@ -318,7 +316,7 @@ impl TurboFn { let ident = &self.ident; let output = &self.output; if let Some(converted_this) = self.converted_this() { - let converted_inputs = self.converted_inputs(); + let inputs = self.inputs(); parse_quote! { { <#output as turbo_tasks::task::TaskOutput>::try_from_raw_vc( @@ -326,7 +324,7 @@ impl TurboFn { *#trait_type_id_ident, std::borrow::Cow::Borrowed(stringify!(#ident)), #converted_this, - turbo_tasks::TaskInput::into_concrete((#converted_inputs)), + Box::new((#(#inputs,)*)) as Box, ) ) } @@ -344,7 +342,7 @@ impl TurboFn { /// given native function. pub fn static_block(&self, native_function_id_ident: &Ident) -> Block { let output = &self.output; - let converted_inputs = self.converted_inputs(); + let inputs = self.inputs(); if let Some(converted_this) = self.converted_this() { parse_quote! { { @@ -352,7 +350,7 @@ impl TurboFn { turbo_tasks::dynamic_this_call( *#native_function_id_ident, #converted_this, - turbo_tasks::TaskInput::into_concrete((#converted_inputs)), + Box::new((#(#inputs,)*)) as Box, ) ) } @@ -363,7 +361,7 @@ impl TurboFn { <#output as turbo_tasks::task::TaskOutput>::try_from_raw_vc( turbo_tasks::dynamic_call( *#native_function_id_ident, - turbo_tasks::TaskInput::into_concrete((#converted_inputs)), + Box::new((#(#inputs,)*)) as Box, ) ) } @@ -394,7 +392,7 @@ fn expand_vc_return_type(orig_output: &Type) -> Type { new_output = match new_output { Type::Group(TypeGroup { elem, .. }) => *elem, Type::Tuple(TypeTuple { elems, .. }) if elems.is_empty() => { - Type::Path(parse_quote!(::turbo_tasks::Vc<()>)) + Type::Path(parse_quote!(turbo_tasks::Vc<()>)) } Type::Path(TypePath { qself: None, diff --git a/crates/turbo-tasks-macros/src/value_impl_macro.rs b/crates/turbo-tasks-macros/src/value_impl_macro.rs index 235268169d125..93eff9cdece0f 100644 --- a/crates/turbo-tasks-macros/src/value_impl_macro.rs +++ b/crates/turbo-tasks-macros/src/value_impl_macro.rs @@ -252,7 +252,6 @@ pub fn value_impl(args: TokenStream, input: TokenStream) -> TokenStream { all_definitions.push(quote! { #[doc(hidden)] #[allow(non_camel_case_types)] - // #[turbo_tasks::async_trait] trait #inline_extension_trait_ident: std::marker::Send { #[allow(declare_interior_mutable_const)] #[doc(hidden)] @@ -267,7 +266,6 @@ pub fn value_impl(args: TokenStream, input: TokenStream) -> TokenStream { } #[doc(hidden)] - // #[turbo_tasks::async_trait] impl #impl_generics #inline_extension_trait_ident for #ty #where_clause { #[allow(declare_interior_mutable_const)] #[doc(hidden)] diff --git a/crates/turbo-tasks-macros/src/value_macro.rs b/crates/turbo-tasks-macros/src/value_macro.rs index 10155d62625cf..9e79117540894 100644 --- a/crates/turbo-tasks-macros/src/value_macro.rs +++ b/crates/turbo-tasks-macros/src/value_macro.rs @@ -241,7 +241,7 @@ pub fn value(args: TokenStream, input: TokenStream) -> TokenStream { // effectively appending to. If there's not, rustdoc will strip // the leading whitespace. let doc_str = format!( - "\n\nThis is a [transparent value type][::turbo_tasks::value#transparent] \ + "\n\nThis is a [transparent value type][turbo_tasks::value#transparent] \ wrapping [`{}`].", inner_type_string, ); diff --git a/crates/turbo-tasks-macros/src/value_trait_macro.rs b/crates/turbo-tasks-macros/src/value_trait_macro.rs index 931c26e69944d..5eabf273b7851 100644 --- a/crates/turbo-tasks-macros/src/value_trait_macro.rs +++ b/crates/turbo-tasks-macros/src/value_trait_macro.rs @@ -64,7 +64,7 @@ pub fn value_trait(args: TokenStream, input: TokenStream) -> TokenStream { let trait_type_ident = get_trait_type_ident(trait_ident); let trait_type_id_ident = get_trait_type_id_ident(trait_ident); let mut dynamic_trait_fns = Vec::new(); - let mut default_method_registers: Vec = Vec::new(); + let mut trait_methods: Vec = Vec::new(); let mut native_functions = Vec::new(); let mut items = Vec::with_capacity(raw_items.len()); @@ -93,6 +93,7 @@ pub fn value_trait(args: TokenStream, input: TokenStream) -> TokenStream { }; let turbo_signature = turbo_fn.signature(); + let arg_types = turbo_fn.input_types(); let dynamic_block = turbo_fn.dynamic_block(&trait_type_id_ident); dynamic_trait_fns.push(quote! { #turbo_signature #dynamic_block @@ -124,14 +125,13 @@ pub fn value_trait(args: TokenStream, input: TokenStream) -> TokenStream { #native_function_ident }); - default_method_registers.push(quote! { - trait_type.register_default_trait_method(stringify!(#ident).into(), *#native_function_id_ident); + trait_methods.push(quote! { + trait_type.register_default_trait_method::<(#(#arg_types),*)>(stringify!(#ident).into(), *#native_function_id_ident); }); native_functions.push(quote! { #[doc(hidden)] #[allow(non_camel_case_types)] - // #[turbo_tasks::async_trait] trait #inline_extension_trait_ident: std::marker::Send { #[allow(declare_interior_mutable_const)] const #native_function_ident: #native_function_ty; @@ -143,7 +143,6 @@ pub fn value_trait(args: TokenStream, input: TokenStream) -> TokenStream { } #[doc(hidden)] - // #[turbo_tasks::async_trait] // Needs to be explicit 'static here, otherwise we can get a lifetime error // in the inline signature. impl #inline_extension_trait_ident for Box { @@ -164,6 +163,9 @@ pub fn value_trait(args: TokenStream, input: TokenStream) -> TokenStream { Some(turbo_fn.static_block(&native_function_id_ident)) } else { + trait_methods.push(quote! { + trait_type.register_trait_method::<(#(#arg_types),*)>(stringify!(#ident).into()); + }); None }; @@ -177,21 +179,8 @@ pub fn value_trait(args: TokenStream, input: TokenStream) -> TokenStream { let value_debug_impl = if debug { quote! { - #[turbo_tasks::value_impl] - impl turbo_tasks::debug::ValueDebug for Box { - #[turbo_tasks::function] - pub fn dbg(self: turbo_tasks::Vc) -> turbo_tasks::Vc { - use turbo_tasks::debug::ValueDebug; - self.dbg_depth(usize::MAX) - } - - #[turbo_tasks::function] - pub async fn dbg_depth(self: turbo_tasks::Vc, depth: usize) -> anyhow::Result> { - use turbo_tasks::debug::ValueDebugFormat; - let string = self.value_debug_format(depth).try_to_value_debug_string().await?.await?; - Ok(turbo_tasks::debug::ValueDebugString::new(format!(concat!(stringify!(#trait_ident), "({})"), string))) - } - } + unsafe impl turbo_tasks::Dynamic> for Box {} + unsafe impl turbo_tasks::Upcast> for Box {} } } else { quote! {} @@ -204,6 +193,10 @@ pub fn value_trait(args: TokenStream, input: TokenStream) -> TokenStream { }); } extended_supertraits.push(quote!(::std::marker::Send)); + extended_supertraits.push(quote!(::std::marker::Sync)); + if debug { + extended_supertraits.push(quote!(turbo_tasks::debug::ValueDebug)); + } let expanded = quote! { #[must_use] @@ -219,7 +212,7 @@ pub fn value_trait(args: TokenStream, input: TokenStream) -> TokenStream { pub(crate) static #trait_type_ident: turbo_tasks::macro_helpers::Lazy = turbo_tasks::macro_helpers::Lazy::new(|| { let mut trait_type = turbo_tasks::TraitType::new(stringify!(#trait_ident).to_string());; - #(#default_method_registers)* + #(#trait_methods)* trait_type }); #[doc(hidden)] diff --git a/crates/turbo-tasks-memory/src/memory_backend.rs b/crates/turbo-tasks-memory/src/memory_backend.rs index 19560c4ae809c..10073a89ef5af 100644 --- a/crates/turbo-tasks-memory/src/memory_backend.rs +++ b/crates/turbo-tasks-memory/src/memory_backend.rs @@ -294,12 +294,13 @@ impl Backend for MemoryBackend { DEPENDENCIES_TO_TRACK.scope(RefCell::new(TaskEdgesSet::new()), future) } - fn try_start_task_execution( - &self, + fn try_start_task_execution<'a>( + &'a self, task: TaskId, turbo_tasks: &dyn TurboTasksBackendApi, - ) -> Option { - self.with_task(task, |task| task.execute(self, turbo_tasks)) + ) -> Option> { + let task = self.task(task); + task.execute(self, turbo_tasks) } fn task_execution_result( @@ -608,8 +609,7 @@ impl Backend for MemoryBackend { }); // It's important to avoid overallocating memory as this will go into the task // cache and stay there forever. We can to be as small as possible. - let (task_type_hash, mut task_type) = PreHashed::into_parts(task_type); - task_type.shrink_to_fit(); + let (task_type_hash, task_type) = PreHashed::into_parts(task_type); let task_type = Arc::new(PreHashed::new(task_type_hash, task_type)); // slow pass with key lock let id = turbo_tasks.get_fresh_task_id(); diff --git a/crates/turbo-tasks-memory/src/task.rs b/crates/turbo-tasks-memory/src/task.rs index d424971f903cf..53791e9b9e2d4 100644 --- a/crates/turbo-tasks-memory/src/task.rs +++ b/crates/turbo-tasks-memory/src/task.rs @@ -695,11 +695,11 @@ impl Task { TaskMetaStateWriteGuard::partial_from(self.state.write()) } - pub(crate) fn execute( - self: &Task, - backend: &MemoryBackend, + pub(crate) fn execute<'a>( + self: &'a Task, + backend: &'a MemoryBackend, turbo_tasks: &dyn TurboTasksBackendApi, - ) -> Option { + ) -> Option> { let mut aggregation_context = TaskAggregationContext::new(turbo_tasks, backend); let (future, span) = { let mut state = self.full_state_mut(); @@ -740,11 +740,14 @@ impl Task { } /// Prepares task execution and returns a future that will execute the task. - fn make_execution_future( - self: &Task, + fn make_execution_future<'a>( + self: &'a Task, _backend: &MemoryBackend, turbo_tasks: &dyn TurboTasksBackendApi, - ) -> (Pin> + Send>>, Span) { + ) -> ( + Pin> + Send + 'a>>, + Span, + ) { match &self.ty { TaskType::Root(bound_fn) => { (bound_fn(), tracing::trace_span!("turbo_tasks::root_task")) @@ -757,31 +760,29 @@ impl Task { PersistentTaskType::Native { fn_type: native_fn, this, - arg: inputs, + arg, } => { let func = registry::get_function(*native_fn); let span = func.span(); let entered = span.enter(); - let bound_fn = func.bind(*this, inputs); - let future = bound_fn(); + let future = func.execute(*this, &**arg); drop(entered); (future, span) } PersistentTaskType::ResolveNative { fn_type: ref native_fn_id, this, - arg: inputs, + arg, } => { let native_fn_id = *native_fn_id; let func = registry::get_function(native_fn_id); let span = func.resolve_span(); let entered = span.enter(); - let inputs = inputs.clone(); let turbo_tasks = turbo_tasks.pin(); let future = Box::pin(PersistentTaskType::run_resolve_native( native_fn_id, *this, - inputs, + &**arg, turbo_tasks, )); drop(entered); @@ -791,20 +792,19 @@ impl Task { trait_type: trait_type_id, method_name: name, this, - arg: inputs, + arg, } => { let trait_type_id = *trait_type_id; let trait_type = registry::get_trait(trait_type_id); let span = trait_type.resolve_span(name); let entered = span.enter(); let name = name.clone(); - let inputs = inputs.clone(); let turbo_tasks = turbo_tasks.pin(); let future = Box::pin(PersistentTaskType::run_resolve_trait( trait_type_id, name, *this, - inputs, + &**arg, turbo_tasks, )); drop(entered); diff --git a/crates/turbo-tasks-memory/tests/all_in_one.rs b/crates/turbo-tasks-memory/tests/all_in_one.rs index f83d05b6ca8e9..b588f2bfe4930 100644 --- a/crates/turbo-tasks-memory/tests/all_in_one.rs +++ b/crates/turbo-tasks-memory/tests/all_in_one.rs @@ -1,6 +1,6 @@ #![feature(arbitrary_self_types)] -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, bail, Result}; use indexmap::{IndexMap, IndexSet}; use turbo_tasks::{debug::ValueDebug, RcStr, Value, ValueToString, Vc}; use turbo_tasks_testing::{register, run}; @@ -35,7 +35,7 @@ async fn all_in_one() { let a: Vc = Vc::cell(32); let b: Vc = Vc::cell(10); - let c: Vc = a.add(b); + let c: Vc = a.add(Vc::upcast(b)); assert_eq!(*c.await?, 42); @@ -107,11 +107,11 @@ async fn all_in_one() { } #[turbo_tasks::value(transparent, serialization = "auto_for_input")] -#[derive(Debug, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Hash)] struct MyTransparentValue(u32); #[turbo_tasks::value(shared, serialization = "auto_for_input")] -#[derive(Debug, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Hash)] enum MyEnumValue { Yeah(u32), Nah, @@ -215,7 +215,7 @@ async fn my_function( #[turbo_tasks::value_trait] trait Add { - fn add(self: Vc, other: Vc) -> Vc; + fn add(self: Vc, other: Vc>) -> Vc; } #[turbo_tasks::value(transparent)] @@ -224,7 +224,10 @@ struct Number(u32); #[turbo_tasks::value_impl] impl Add for Number { #[turbo_tasks::function] - async fn add(self: Vc, other: Vc) -> Result> { + async fn add(self: Vc, other: Vc>) -> Result> { + let Some(other) = Vc::try_resolve_downcast_type::(other).await? else { + bail!("Expected Number"); + }; Ok(Vc::cell(*self.await? + *other.await?)) } } @@ -235,7 +238,10 @@ struct NumberB(u32); #[turbo_tasks::value_impl] impl Add for NumberB { #[turbo_tasks::function] - async fn add(self: Vc, other: Vc) -> Result> { + async fn add(self: Vc, other: Vc>) -> Result> { + let Some(other) = Vc::try_resolve_downcast_type::(other).await? else { + bail!("Expected NumberB"); + }; Ok(Vc::cell(*self.await? + *other.await?)) } } diff --git a/crates/turbo-tasks-memory/tests/debug.rs b/crates/turbo-tasks-memory/tests/debug.rs index 3959cb036ed74..ecc2c1c6e4d11 100644 --- a/crates/turbo-tasks-memory/tests/debug.rs +++ b/crates/turbo-tasks-memory/tests/debug.rs @@ -27,7 +27,7 @@ async fn transparent_debug() { async fn enum_none_debug() { run! { let a: Vc = Enum::None.cell(); - assert_eq!(format!("{:?}", a.dbg().await?), "None"); + assert_eq!(format!("{:?}", a.dbg().await?), "Enum::None"); } } @@ -35,7 +35,7 @@ async fn enum_none_debug() { async fn enum_transparent_debug() { run! { let a: Vc = Enum::Transparent(Transparent(42).cell()).cell(); - assert_eq!(format!("{:?}", a.dbg().await?), r#"Transparent( + assert_eq!(format!("{:?}", a.dbg().await?), r#"Enum::Transparent( 42, )"#); } @@ -45,8 +45,8 @@ async fn enum_transparent_debug() { async fn enum_inner_vc_debug() { run! { let a: Vc = Enum::Enum(Enum::None.cell()).cell(); - assert_eq!(format!("{:?}", a.dbg().await?), r#"Enum( - None, + assert_eq!(format!("{:?}", a.dbg().await?), r#"Enum::Enum( + Enum::None, )"#); } } diff --git a/crates/turbo-tasks-testing/src/lib.rs b/crates/turbo-tasks-testing/src/lib.rs index 2c0e62f88c67f..353d0a1b3c7be 100644 --- a/crates/turbo-tasks-testing/src/lib.rs +++ b/crates/turbo-tasks-testing/src/lib.rs @@ -20,7 +20,8 @@ use turbo_tasks::{ registry, test_helpers::with_turbo_tasks_for_testing, util::{SharedError, StaticOrArc}, - CellId, InvalidationReason, RawVc, TaskId, TraitTypeId, TurboTasksApi, TurboTasksCallApi, + CellId, InvalidationReason, MagicAny, RawVc, TaskId, TraitTypeId, TurboTasksApi, + TurboTasksCallApi, }; enum Task { @@ -40,12 +41,11 @@ impl VcStorage { &self, func: turbo_tasks::FunctionId, this_arg: Option, - arg: turbo_tasks::ConcreteTaskInput, + arg: Box, ) -> RawVc { let this = self.this.upgrade().unwrap(); - let func = registry::get_function(func).bind(this_arg, &arg); let handle = tokio::runtime::Handle::current(); - let future = func(); + let future = registry::get_function(func).execute(this_arg, &*arg); let i = { let mut tasks = self.tasks.lock().unwrap(); let i = tasks.len(); @@ -80,11 +80,7 @@ impl VcStorage { } impl TurboTasksCallApi for VcStorage { - fn dynamic_call( - &self, - func: turbo_tasks::FunctionId, - arg: turbo_tasks::ConcreteTaskInput, - ) -> RawVc { + fn dynamic_call(&self, func: turbo_tasks::FunctionId, arg: Box) -> RawVc { self.dynamic_call(func, None, arg) } @@ -92,16 +88,12 @@ impl TurboTasksCallApi for VcStorage { &self, func: turbo_tasks::FunctionId, this_arg: RawVc, - arg: turbo_tasks::ConcreteTaskInput, + arg: Box, ) -> RawVc { self.dynamic_call(func, Some(this_arg), arg) } - fn native_call( - &self, - _func: turbo_tasks::FunctionId, - _arg: turbo_tasks::ConcreteTaskInput, - ) -> RawVc { + fn native_call(&self, _func: turbo_tasks::FunctionId, _arg: Box) -> RawVc { unreachable!() } @@ -109,7 +101,7 @@ impl TurboTasksCallApi for VcStorage { &self, _func: turbo_tasks::FunctionId, _this: RawVc, - _arg: turbo_tasks::ConcreteTaskInput, + _arg: Box, ) -> RawVc { unreachable!() } @@ -119,7 +111,7 @@ impl TurboTasksCallApi for VcStorage { _trait_type: turbo_tasks::TraitTypeId, _trait_fn_name: Cow<'static, str>, _this: RawVc, - _arg: turbo_tasks::ConcreteTaskInput, + _arg: Box, ) -> RawVc { unreachable!() } diff --git a/crates/turbo-tasks/src/backend.rs b/crates/turbo-tasks/src/backend.rs index aa52277f0a140..6e8194ed2204a 100644 --- a/crates/turbo-tasks/src/backend.rs +++ b/crates/turbo-tasks/src/backend.rs @@ -18,12 +18,13 @@ use tracing::Span; pub use crate::id::{BackendJobId, ExecutionId}; use crate::{ event::EventListener, + magic_any::MagicAny, manager::TurboTasksBackendApi, raw_vc::CellId, registry, trait_helpers::{get_trait_method, has_trait, traits}, - ConcreteTaskInput, FunctionId, RawVc, ReadRef, SharedReference, TaskId, TaskIdProvider, - TaskIdSet, TraitRef, TraitTypeId, ValueTypeId, VcValueTrait, VcValueType, + FunctionId, RawVc, ReadRef, SharedReference, TaskId, TaskIdProvider, TaskIdSet, TraitRef, + TraitTypeId, ValueTypeId, VcValueTrait, VcValueType, }; pub enum TaskType { @@ -65,13 +66,13 @@ impl Debug for TransientTaskType { } } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Hash)] pub enum PersistentTaskType { /// A normal task execution a native (rust) function Native { fn_type: FunctionId, this: Option, - arg: ConcreteTaskInput, + arg: Box, }, /// A resolve task, which resolves arguments and calls the function with @@ -79,7 +80,7 @@ pub enum PersistentTaskType { ResolveNative { fn_type: FunctionId, this: Option, - arg: ConcreteTaskInput, + arg: Box, }, /// A trait method resolve task. It resolves the first (`self`) argument and @@ -90,7 +91,7 @@ pub enum PersistentTaskType { trait_type: TraitTypeId, method_name: Cow<'static, str>, this: RawVc, - arg: ConcreteTaskInput, + arg: Box, }, } @@ -100,28 +101,206 @@ impl Display for PersistentTaskType { } } -impl PersistentTaskType { - pub fn shrink_to_fit(&mut self) { - match self { - Self::Native { - fn_type: _, - this: _, - arg, - } => arg.shrink_to_fit(), - Self::ResolveNative { - fn_type: _, - this: _, - arg, - } => arg.shrink_to_fit(), - Self::ResolveTrait { - trait_type: _, - method_name: _, - this: _, - arg, - } => arg.shrink_to_fit(), +mod ser { + use serde::{ + ser::{SerializeSeq, SerializeTuple}, + Deserialize, Deserializer, Serialize, Serializer, + }; + + use super::*; + + enum FunctionAndArg<'a> { + Owned { + fn_type: FunctionId, + arg: Box, + }, + Borrowed { + fn_type: FunctionId, + arg: &'a dyn MagicAny, + }, + } + + impl<'a> Serialize for FunctionAndArg<'a> { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + let FunctionAndArg::Borrowed { fn_type, arg } = self else { + unreachable!(); + }; + let mut state = serializer.serialize_seq(Some(2))?; + state.serialize_element(&fn_type)?; + let arg = *arg; + let arg = registry::get_function(*fn_type).arg_meta.as_serialize(arg); + state.serialize_element(arg)?; + state.end() + } + } + + impl<'de> Deserialize<'de> for FunctionAndArg<'de> { + fn deserialize>(deserializer: D) -> Result { + struct Visitor; + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = FunctionAndArg<'de>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a valid PersistentTaskType") + } + + fn visit_seq(self, mut seq: A) -> std::result::Result + where + A: serde::de::SeqAccess<'de>, + { + let fn_type = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; + let seed = registry::get_function(fn_type) + .arg_meta + .deserialization_seed(); + let arg = seq + .next_element_seed(seed)? + .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?; + Ok(FunctionAndArg::Owned { fn_type, arg }) + } + } + deserializer.deserialize_seq(Visitor) + } + } + + impl Serialize for PersistentTaskType { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: ser::Serializer, + { + match self { + PersistentTaskType::Native { fn_type, this, arg } => { + let mut s = serializer.serialize_seq(Some(3))?; + s.serialize_element::(&0)?; + s.serialize_element(&FunctionAndArg::Borrowed { + fn_type: *fn_type, + arg, + })?; + s.serialize_element(this)?; + s.end() + } + PersistentTaskType::ResolveNative { fn_type, this, arg } => { + let mut s = serializer.serialize_seq(Some(3))?; + s.serialize_element::(&1)?; + s.serialize_element(&FunctionAndArg::Borrowed { + fn_type: *fn_type, + arg, + })?; + s.serialize_element(this)?; + s.end() + } + PersistentTaskType::ResolveTrait { + trait_type, + method_name, + this, + arg, + } => { + let mut s = serializer.serialize_tuple(5)?; + s.serialize_element::(&2)?; + s.serialize_element(trait_type)?; + s.serialize_element(method_name)?; + s.serialize_element(this)?; + let arg = if let Some(method) = + registry::get_trait(*trait_type).methods.get(method_name) + { + method.arg_serializer.as_serialize(arg) + } else { + return Err(serde::ser::Error::custom("Method not found")); + }; + s.serialize_element(arg)?; + s.end() + } + } + } + } + + impl<'de> Deserialize<'de> for PersistentTaskType { + fn deserialize>(deserializer: D) -> Result { + #[derive(Deserialize)] + enum VariantKind { + Native, + ResolveNative, + ResolveTrait, + } + struct Visitor; + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = PersistentTaskType; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a valid PersistentTaskType") + } + + fn visit_seq(self, mut seq: A) -> std::result::Result + where + A: serde::de::SeqAccess<'de>, + { + let kind = seq + .next_element::()? + .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; + match kind { + 0 => { + let FunctionAndArg::Owned { fn_type, arg } = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(1, &self))? + else { + unreachable!(); + }; + let this = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?; + Ok(PersistentTaskType::Native { fn_type, this, arg }) + } + 1 => { + let FunctionAndArg::Owned { fn_type, arg } = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(1, &self))? + else { + unreachable!(); + }; + let this = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?; + Ok(PersistentTaskType::ResolveNative { fn_type, this, arg }) + } + 2 => { + let trait_type = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; + let method_name = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?; + let this = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?; + let Some(method) = + registry::get_trait(trait_type).methods.get(&method_name) + else { + return Err(serde::de::Error::custom("Method not found")); + }; + let arg = seq + .next_element_seed(method.arg_deserializer)? + .ok_or_else(|| serde::de::Error::invalid_length(3, &self))?; + Ok(PersistentTaskType::ResolveTrait { + trait_type, + method_name, + this, + arg, + }) + } + _ => Err(serde::de::Error::custom("Invalid variant")), + } + } + } + deserializer.deserialize_seq(Visitor) } } +} +impl PersistentTaskType { /// Returns the name of the function in the code. Trait methods are /// formatted as `TraitName::method_name`. /// @@ -149,8 +328,8 @@ impl PersistentTaskType { } } -pub struct TaskExecutionSpec { - pub future: Pin> + Send>>, +pub struct TaskExecutionSpec<'a> { + pub future: Pin> + Send + 'a>>, pub span: Span, } @@ -232,11 +411,11 @@ pub trait Backend: Sync + Send { future: T, ) -> Self::ExecutionScopeFuture; - fn try_start_task_execution( - &self, + fn try_start_task_execution<'a>( + &'a self, task: TaskId, turbo_tasks: &dyn TurboTasksBackendApi, - ) -> Option; + ) -> Option>; fn task_execution_result( &self, @@ -376,13 +555,13 @@ impl PersistentTaskType { pub async fn run_resolve_native( fn_id: FunctionId, mut this: Option, - arg: ConcreteTaskInput, + arg: &dyn MagicAny, turbo_tasks: Arc>, ) -> Result { if let Some(this) = this.as_mut() { *this = this.resolve().await?; } - let arg = arg.resolve().await?; + let arg = registry::get_function(fn_id).arg_meta.resolve(arg).await?; Ok(if let Some(this) = this { turbo_tasks.this_call(fn_id, this, arg) } else { @@ -406,7 +585,7 @@ impl PersistentTaskType { trait_type: TraitTypeId, name: Cow<'static, str>, this: RawVc, - arg: ConcreteTaskInput, + arg: &dyn MagicAny, turbo_tasks: Arc>, ) -> Result { let this = this.resolve().await?; @@ -418,7 +597,10 @@ impl PersistentTaskType { }; let native_fn = Self::resolve_trait_method_from_value(trait_type, this_ty, name)?; - let arg = arg.resolve().await?; + let arg = registry::get_function(native_fn) + .arg_meta + .resolve(arg) + .await?; Ok(turbo_tasks.dynamic_this_call(native_fn, this, arg)) } @@ -454,40 +636,6 @@ impl PersistentTaskType { } } } - - pub fn run( - self, - turbo_tasks: Arc>, - ) -> Pin> + Send>> { - match self { - PersistentTaskType::Native { - fn_type: fn_id, - this, - arg, - } => { - let native_fn = registry::get_function(fn_id); - let bound = native_fn.bind(this, &arg); - (bound)() - } - PersistentTaskType::ResolveNative { - fn_type: fn_id, - this, - arg: inputs, - } => Box::pin(Self::run_resolve_native(fn_id, this, inputs, turbo_tasks)), - PersistentTaskType::ResolveTrait { - trait_type, - method_name: name, - this, - arg: inputs, - } => Box::pin(Self::run_resolve_trait( - trait_type, - name, - this, - inputs, - turbo_tasks, - )), - } - } } #[cfg(test)] @@ -512,7 +660,7 @@ pub(crate) mod tests { PersistentTaskType::Native { fn_type: *MOCK_FUNC_TASK_FUNCTION_ID, this: None, - arg: Default::default() + arg: Box::new(()), } .get_name(), "mock_func_task", @@ -522,7 +670,7 @@ pub(crate) mod tests { trait_type: *MOCKTRAIT_TRAIT_TYPE_ID, method_name: "mock_method_task".into(), this: RawVc::TaskOutput(unsafe { TaskId::new_unchecked(1) }), - arg: Default::default() + arg: Box::new(()), } .get_name(), "MockTrait::mock_method_task", diff --git a/crates/turbo-tasks/src/lib.rs b/crates/turbo-tasks/src/lib.rs index aa60ab25ab6da..198bd4d870045 100644 --- a/crates/turbo-tasks/src/lib.rs +++ b/crates/turbo-tasks/src/lib.rs @@ -33,6 +33,7 @@ #![feature(arbitrary_self_types)] #![feature(type_alias_impl_trait)] #![feature(never_type)] +#![feature(impl_trait_in_assoc_type)] pub mod backend; mod capture_future; @@ -85,6 +86,7 @@ pub use invalidation::{ DynamicEqHash, InvalidationReason, InvalidationReasonKind, InvalidationReasonSet, }; pub use join_iter_ext::{JoinIterExt, TryFlatJoinIterExt, TryJoinIterExt}; +pub use magic_any::MagicAny; pub use manager::{ dynamic_call, dynamic_this_call, emit, get_invalidator, mark_finished, mark_stateful, prevent_gc, run_once, run_once_with_reason, spawn_blocking, spawn_thread, trait_call, @@ -96,10 +98,7 @@ pub use raw_vc::{CellId, RawVc, ReadRawVcFuture, ResolveTypeError}; pub use read_ref::ReadRef; use rustc_hash::FxHasher; pub use state::State; -pub use task::{ - concrete_task_input::{ConcreteTaskInput, SharedReference, SharedValue}, - task_input::TaskInput, -}; +pub use task::{task_input::TaskInput, SharedReference}; pub use trait_ref::{IntoTraitRef, TraitRef}; pub use turbo_tasks_macros::{function, value, value_impl, value_trait, TaskInput}; pub use value::{TransientInstance, TransientValue, Value}; diff --git a/crates/turbo-tasks/src/macro_helpers.rs b/crates/turbo-tasks/src/macro_helpers.rs index 3bd45451b8f27..00b58c2cfbf42 100644 --- a/crates/turbo-tasks/src/macro_helpers.rs +++ b/crates/turbo-tasks/src/macro_helpers.rs @@ -1,9 +1,13 @@ //! Runtime helpers for [turbo-tasks-macro]. +pub use async_trait::async_trait; pub use once_cell::sync::{Lazy, OnceCell}; pub use serde; pub use tracing; -pub use super::manager::{find_cell_by_type, notify_scheduled_tasks, spawn_detached}; +pub use super::{ + magic_any::MagicAny, + manager::{find_cell_by_type, notify_scheduled_tasks, spawn_detached}, +}; use crate::debug::ValueDebugFormatString; #[inline(never)] @@ -16,3 +20,10 @@ pub async fn value_debug_format_field(value: ValueDebugFormatString<'_>) -> Stri Err(err) => format!("{0:?}", err), } } + +#[macro_export] +macro_rules! stringify_path { + ($path:path) => { + stringify!($path) + }; +} diff --git a/crates/turbo-tasks/src/magic_any.rs b/crates/turbo-tasks/src/magic_any.rs index d2f48ed7a9720..7c2af51ef9253 100644 --- a/crates/turbo-tasks/src/magic_any.rs +++ b/crates/turbo-tasks/src/magic_any.rs @@ -1,7 +1,6 @@ use core::fmt; use std::{ any::{Any, TypeId}, - cmp::Ordering, fmt::Debug, hash::{Hash, Hasher}, ops::DerefMut, @@ -19,8 +18,6 @@ pub trait MagicAny: mopa::Any + Send + Sync { fn magic_hash(&self, hasher: &mut dyn Hasher); - fn magic_cmp(&self, other: &dyn MagicAny) -> Ordering; - #[cfg(debug_assertions)] fn magic_type_name(&self) -> &'static str; } @@ -34,7 +31,7 @@ mod clippy { mopafy!(MagicAny); } -impl MagicAny for T { +impl MagicAny for T { fn magic_any_arc(self: Arc) -> Arc { self } @@ -61,13 +58,6 @@ impl MagicAny for T { Hash::hash(&(TypeId::of::(), self), &mut HasherMut(hasher)) } - fn magic_cmp(&self, other: &dyn MagicAny) -> Ordering { - match other.downcast_ref::() { - None => Ord::cmp(&TypeId::of::(), &other.type_id()), - Some(other) => self.cmp(other), - } - } - #[cfg(debug_assertions)] fn magic_type_name(&self) -> &'static str { std::any::type_name::() @@ -88,18 +78,6 @@ impl PartialEq for dyn MagicAny { impl Eq for dyn MagicAny {} -impl PartialOrd for dyn MagicAny { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for dyn MagicAny { - fn cmp(&self, other: &Self) -> Ordering { - self.magic_cmp(other) - } -} - impl Hash for dyn MagicAny { fn hash(&self, hasher: &mut H) { self.magic_hash(hasher) @@ -122,7 +100,7 @@ where } impl dyn MagicAny { - pub fn as_serialize( + pub fn as_serialize( &self, ) -> &dyn erased_serde::Serialize { if let Some(r) = self.downcast_ref::() { @@ -140,6 +118,30 @@ impl dyn MagicAny { } } +type MagicAnySerializeFunctor = fn(&dyn MagicAny) -> &dyn erased_serde::Serialize; + +#[derive(Clone, Copy)] +pub struct MagicAnySerializeSeed { + functor: MagicAnySerializeFunctor, +} + +impl MagicAnySerializeSeed { + pub fn new() -> Self { + fn serialize( + value: &dyn MagicAny, + ) -> &dyn erased_serde::Serialize { + value.as_serialize::() + } + Self { + functor: serialize::, + } + } + + pub fn as_serialize<'a>(&self, value: &'a dyn MagicAny) -> &'a dyn erased_serde::Serialize { + (self.functor)(value) + } +} + type MagicAnyDeserializeSeedFunctor = fn(&mut dyn erased_serde::Deserializer<'_>) -> Result, erased_serde::Error>; @@ -151,11 +153,9 @@ pub struct MagicAnyDeserializeSeed { impl MagicAnyDeserializeSeed { pub fn new() -> Self where - T: for<'de> Deserialize<'de> + Debug + Eq + Ord + Hash + Send + Sync + 'static, + T: for<'de> Deserialize<'de> + Debug + Eq + Hash + Send + Sync + 'static, { - fn deserialize< - T: Debug + Eq + Ord + Hash + for<'de> Deserialize<'de> + Send + Sync + 'static, - >( + fn deserialize Deserialize<'de> + Send + Sync + 'static>( deserializer: &mut dyn erased_serde::Deserializer<'_>, ) -> Result, erased_serde::Error> { let value: T = erased_serde::deserialize(deserializer)?; diff --git a/crates/turbo-tasks/src/manager.rs b/crates/turbo-tasks/src/manager.rs index 927438749d440..eef0d22893ee1 100644 --- a/crates/turbo-tasks/src/manager.rs +++ b/crates/turbo-tasks/src/manager.rs @@ -32,26 +32,28 @@ use crate::{ event::{Event, EventListener}, id::{BackendJobId, FunctionId, TraitTypeId}, id_factory::IdFactoryWithReuse, + magic_any::MagicAny, raw_vc::{CellId, RawVc}, + registry, trace::TraceRawVcs, trait_helpers::get_trait_method, util::StaticOrArc, vc::ReadVcFuture, - Completion, ConcreteTaskInput, InvalidationReason, InvalidationReasonSet, SharedReference, - TaskId, TaskIdSet, ValueTypeId, Vc, VcRead, VcValueTrait, VcValueType, + Completion, InvalidationReason, InvalidationReasonSet, SharedReference, TaskId, TaskIdSet, + ValueTypeId, Vc, VcRead, VcValueTrait, VcValueType, }; pub trait TurboTasksCallApi: Sync + Send { - fn dynamic_call(&self, func: FunctionId, arg: ConcreteTaskInput) -> RawVc; - fn dynamic_this_call(&self, func: FunctionId, this: RawVc, arg: ConcreteTaskInput) -> RawVc; - fn native_call(&self, func: FunctionId, arg: ConcreteTaskInput) -> RawVc; - fn this_call(&self, func: FunctionId, this: RawVc, arg: ConcreteTaskInput) -> RawVc; + fn dynamic_call(&self, func: FunctionId, arg: Box) -> RawVc; + fn dynamic_this_call(&self, func: FunctionId, this: RawVc, arg: Box) -> RawVc; + fn native_call(&self, func: FunctionId, arg: Box) -> RawVc; + fn this_call(&self, func: FunctionId, this: RawVc, arg: Box) -> RawVc; fn trait_call( &self, trait_type: TraitTypeId, trait_fn_name: Cow<'static, str>, this: RawVc, - arg: ConcreteTaskInput, + arg: Box, ) -> RawVc; fn run_once( @@ -373,7 +375,7 @@ impl TurboTasks { /// Call a native function with arguments. /// All inputs must be resolved. - pub(crate) fn native_call(&self, func: FunctionId, arg: ConcreteTaskInput) -> RawVc { + pub(crate) fn native_call(&self, func: FunctionId, arg: Box) -> RawVc { RawVc::TaskOutput(self.backend.get_or_create_persistent_task( PersistentTaskType::Native { fn_type: func, @@ -387,7 +389,7 @@ impl TurboTasks { /// Call a native function with arguments. /// All inputs must be resolved. - pub(crate) fn this_call(&self, func: FunctionId, this: RawVc, arg: ConcreteTaskInput) -> RawVc { + pub(crate) fn this_call(&self, func: FunctionId, this: RawVc, arg: Box) -> RawVc { RawVc::TaskOutput(self.backend.get_or_create_persistent_task( PersistentTaskType::Native { fn_type: func, @@ -401,8 +403,8 @@ impl TurboTasks { /// Calls a native function with arguments. Resolves arguments when needed /// with a wrapper task. - pub fn dynamic_call(&self, func: FunctionId, arg: ConcreteTaskInput) -> RawVc { - if arg.is_resolved() { + pub fn dynamic_call(&self, func: FunctionId, arg: Box) -> RawVc { + if registry::get_function(func).arg_meta.is_resolved(&*arg) { self.native_call(func, arg) } else { RawVc::TaskOutput(self.backend.get_or_create_persistent_task( @@ -423,9 +425,9 @@ impl TurboTasks { &self, func: FunctionId, this: RawVc, - arg: ConcreteTaskInput, + arg: Box, ) -> RawVc { - if this.is_resolved() && arg.is_resolved() { + if this.is_resolved() && registry::get_function(func).arg_meta.is_resolved(&*arg) { self.this_call(func, this, arg) } else { RawVc::TaskOutput(self.backend.get_or_create_persistent_task( @@ -447,7 +449,7 @@ impl TurboTasks { trait_type: TraitTypeId, mut trait_fn_name: Cow<'static, str>, this: RawVc, - arg: ConcreteTaskInput, + arg: Box, ) -> RawVc { // avoid creating a wrapper task if self is already resolved // for resolved cells we already know the value type so we can lookup the @@ -852,16 +854,16 @@ impl TurboTasks { } impl TurboTasksCallApi for TurboTasks { - fn dynamic_call(&self, func: FunctionId, arg: ConcreteTaskInput) -> RawVc { + fn dynamic_call(&self, func: FunctionId, arg: Box) -> RawVc { self.dynamic_call(func, arg) } - fn dynamic_this_call(&self, func: FunctionId, this: RawVc, arg: ConcreteTaskInput) -> RawVc { + fn dynamic_this_call(&self, func: FunctionId, this: RawVc, arg: Box) -> RawVc { self.dynamic_this_call(func, this, arg) } - fn native_call(&self, func: FunctionId, arg: ConcreteTaskInput) -> RawVc { + fn native_call(&self, func: FunctionId, arg: Box) -> RawVc { self.native_call(func, arg) } - fn this_call(&self, func: FunctionId, this: RawVc, arg: ConcreteTaskInput) -> RawVc { + fn this_call(&self, func: FunctionId, this: RawVc, arg: Box) -> RawVc { self.this_call(func, this, arg) } fn trait_call( @@ -869,7 +871,7 @@ impl TurboTasksCallApi for TurboTasks { trait_type: TraitTypeId, trait_fn_name: Cow<'static, str>, this: RawVc, - arg: ConcreteTaskInput, + arg: Box, ) -> RawVc { self.trait_call(trait_type, trait_fn_name, this, arg) } @@ -1346,13 +1348,13 @@ pub async fn run_once_with_reason( } /// Calls [`TurboTasks::dynamic_call`] for the current turbo tasks instance. -pub fn dynamic_call(func: FunctionId, arg: ConcreteTaskInput) -> RawVc { +pub fn dynamic_call(func: FunctionId, arg: Box) -> RawVc { with_turbo_tasks(|tt| tt.dynamic_call(func, arg)) } /// Calls [`TurboTasks::dynamic_this_call`] for the current turbo tasks /// instance. -pub fn dynamic_this_call(func: FunctionId, this: RawVc, arg: ConcreteTaskInput) -> RawVc { +pub fn dynamic_this_call(func: FunctionId, this: RawVc, arg: Box) -> RawVc { with_turbo_tasks(|tt| tt.dynamic_this_call(func, this, arg)) } @@ -1361,7 +1363,7 @@ pub fn trait_call( trait_type: TraitTypeId, trait_fn_name: Cow<'static, str>, this: RawVc, - arg: ConcreteTaskInput, + arg: Box, ) -> RawVc { with_turbo_tasks(|tt| tt.trait_call(trait_type, trait_fn_name, this, arg)) } diff --git a/crates/turbo-tasks/src/native_function.rs b/crates/turbo-tasks/src/native_function.rs index 23ec8d2ded991..6a774fc6272e6 100644 --- a/crates/turbo-tasks/src/native_function.rs +++ b/crates/turbo-tasks/src/native_function.rs @@ -1,18 +1,89 @@ -use std::{fmt::Debug, hash::Hash}; +use std::{fmt::Debug, hash::Hash, pin::Pin}; +use anyhow::{Context, Result}; +use futures::Future; +use serde::{Deserialize, Serialize}; use tracing::Span; use crate::{ self as turbo_tasks, + magic_any::{MagicAny, MagicAnyDeserializeSeed, MagicAnySerializeSeed}, registry::register_function, task::{ - function::{IntoTaskFnWithThis, NativeTaskFn}, + function::{IntoTaskFnWithThis, NativeTaskFuture}, IntoTaskFn, TaskFn, }, - util::SharedError, - ConcreteTaskInput, RawVc, + RawVc, TaskInput, }; +type ResolveFunctor = + for<'a> fn( + &'a dyn MagicAny, + ) -> Pin>> + Send + 'a>>; + +type IsResolvedFunctor = fn(&dyn MagicAny) -> bool; + +pub struct ArgMeta { + serializer: MagicAnySerializeSeed, + deserializer: MagicAnyDeserializeSeed, + is_resolved: IsResolvedFunctor, + resolve: ResolveFunctor, +} + +impl ArgMeta { + pub fn new() -> Self + where + T: TaskInput + Serialize + for<'de> Deserialize<'de> + 'static, + { + fn downcast(value: &dyn MagicAny) -> &T + where + T: MagicAny, + { + value + .downcast_ref::() + .with_context(|| { + #[cfg(debug_assertions)] + return format!( + "Invalid argument type, expected {} got {}", + std::any::type_name::(), + value.magic_type_name() + ); + #[cfg(not(debug_assertions))] + return "Invalid argument type"; + }) + .unwrap() + } + Self { + serializer: MagicAnySerializeSeed::new::(), + deserializer: MagicAnyDeserializeSeed::new::(), + is_resolved: |value| downcast::(value).is_resolved(), + resolve: |value| { + Box::pin(async { + let value = downcast::(value); + let resolved = value.resolve().await?; + Ok(Box::new(resolved) as Box) + }) + }, + } + } + + pub fn deserialization_seed(&self) -> MagicAnyDeserializeSeed { + self.deserializer + } + + pub fn as_serialize<'a>(&self, value: &'a dyn MagicAny) -> &'a dyn erased_serde::Serialize { + self.serializer.as_serialize(value) + } + + pub fn is_resolved(&self, value: &dyn MagicAny) -> bool { + (self.is_resolved)(value) + } + + pub async fn resolve(&self, value: &dyn MagicAny) -> Result> { + (self.resolve)(value).await + } +} + /// A native (rust) turbo-tasks function. It's used internally by /// `#[turbo_tasks::function]`. #[turbo_tasks::value(cell = "new", serialization = "none", eq = "manual")] @@ -23,6 +94,9 @@ pub struct NativeFunction { /// handles the task execution. #[turbo_tasks(debug_ignore, trace_ignore)] pub implementation: Box, + + #[turbo_tasks(debug_ignore, trace_ignore)] + pub arg_meta: ArgMeta, } impl Debug for NativeFunction { @@ -36,35 +110,34 @@ impl Debug for NativeFunction { impl NativeFunction { pub fn new_function(name: String, implementation: I) -> Self where + Inputs: TaskInput + Serialize + for<'de> Deserialize<'de> + 'static, I: IntoTaskFn, { Self { name, implementation: Box::new(implementation.into_task_fn()), + arg_meta: ArgMeta::new::(), } } - pub fn new_method(name: String, implementation: I) -> Self + pub fn new_method(name: String, implementation: I) -> Self where - I: IntoTaskFnWithThis, + This: Sync + Send + 'static, + Inputs: TaskInput + Serialize + for<'de> Deserialize<'de> + 'static, + I: IntoTaskFnWithThis, { Self { name, implementation: Box::new(implementation.into_task_fn_with_this()), + arg_meta: ArgMeta::new::(), } } - /// Creates a functor for execution from a fixed set of inputs. - pub fn bind(&'static self, this: Option, arg: &ConcreteTaskInput) -> NativeTaskFn { + /// Executed the function + pub fn execute(&'static self, this: Option, arg: &dyn MagicAny) -> NativeTaskFuture { match (self.implementation).functor(this, arg) { Ok(functor) => functor, - Err(err) => { - let err = SharedError::new(err); - Box::new(move || { - let err = err.clone(); - Box::pin(async { Err(err.into()) }) - }) - } + Err(err) => Box::pin(async { Err(err) }), } } diff --git a/crates/turbo-tasks/src/no_move_vec.rs b/crates/turbo-tasks/src/no_move_vec.rs index 7fde0833bec02..830b809dbe520 100644 --- a/crates/turbo-tasks/src/no_move_vec.rs +++ b/crates/turbo-tasks/src/no_move_vec.rs @@ -11,7 +11,7 @@ const BUCKETS: usize = (usize::BITS + 1) as usize; /// An `Option`-like type that guarantees that a fully zeroed value is a valid /// `None` variant. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[repr(u8)] enum COption { // TODO(alexkirsz) We need a way to guarantee that a fully zeroed value is a diff --git a/crates/turbo-tasks/src/raw_vc.rs b/crates/turbo-tasks/src/raw_vc.rs index 50c4a8ed16c3f..582dec5396dcf 100644 --- a/crates/turbo-tasks/src/raw_vc.rs +++ b/crates/turbo-tasks/src/raw_vc.rs @@ -35,7 +35,7 @@ pub enum ResolveTypeError { ReadError { source: anyhow::Error }, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct CellId { pub type_id: ValueTypeId, pub index: u32, @@ -52,7 +52,7 @@ impl Display for CellId { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum RawVc { TaskOutput(TaskId), TaskCell(TaskId, CellId), diff --git a/crates/turbo-tasks/src/registry.rs b/crates/turbo-tasks/src/registry.rs index 04cc6e995c2ca..e4e0581fe6e80 100644 --- a/crates/turbo-tasks/src/registry.rs +++ b/crates/turbo-tasks/src/registry.rs @@ -31,7 +31,7 @@ static TRAIT_TYPES: Lazy> = Lazy:: fn register_thing< K: TryFrom + Deref + Sync + Send + Copy, - V: Clone + Hash + Ord + Eq + Sync + Send + Copy, + V: Clone + Hash + Eq + Sync + Send + Copy, const INITIAL_CAPACITY_BITS: u32, >( global_name: &'static str, @@ -54,7 +54,7 @@ fn register_thing< fn get_thing_id< K: From + Deref + Sync + Send + Copy + Debug, - V: Clone + Hash + Ord + Eq + Debug + Sync + Send + Debug, + V: Clone + Hash + Eq + Debug + Sync + Send + Debug, >( value: V, map_by_value: &DashMap, diff --git a/crates/turbo-tasks/src/task/concrete_task_input.rs b/crates/turbo-tasks/src/task/concrete_task_input.rs deleted file mode 100644 index 87818b4d73549..0000000000000 --- a/crates/turbo-tasks/src/task/concrete_task_input.rs +++ /dev/null @@ -1,475 +0,0 @@ -use std::{ - any::Any, - fmt::{Debug, Display}, - future::Future, - hash::Hash, - pin::Pin, - sync::Arc, -}; - -use anyhow::Result; -use serde::{ser::SerializeTuple, Deserialize, Serialize}; -use unsize::CoerceUnsize; - -use crate::{ - backend::CellContent, - magic_any::MagicAny, - manager::{read_task_cell, read_task_output}, - registry, - triomphe_utils::{coerce_to_any_send_sync, downcast_triomphe_arc}, - turbo_tasks, CellId, RawVc, RcStr, TaskId, ValueTypeId, -}; - -/// A type-erased wrapper for [`triomphe::Arc`]. -#[derive(Clone)] -pub struct SharedReference( - pub Option, - pub triomphe::Arc, -); - -impl SharedReference { - pub fn new(type_id: Option, data: triomphe::Arc) -> Self { - Self(type_id, data.unsize(coerce_to_any_send_sync())) - } - - pub fn downcast(self) -> Result, Self> { - match downcast_triomphe_arc(self.1) { - Ok(data) => Ok(data), - Err(data) => Err(Self(self.0, data)), - } - } -} - -impl Hash for SharedReference { - fn hash(&self, state: &mut H) { - Hash::hash(&(&*self.1 as *const (dyn Any + Send + Sync)), state) - } -} -impl PartialEq for SharedReference { - // Must compare with PartialEq rather than std::ptr::addr_eq since the latter - // only compares their addresses. - #[allow(ambiguous_wide_pointer_comparisons)] - fn eq(&self, other: &Self) -> bool { - triomphe::Arc::ptr_eq(&self.1, &other.1) - } -} -impl Eq for SharedReference {} -impl PartialOrd for SharedReference { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl Ord for SharedReference { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - Ord::cmp( - &(&*self.1 as *const (dyn Any + Send + Sync)).cast::<()>(), - &(&*other.1 as *const (dyn Any + Send + Sync)).cast::<()>(), - ) - } -} -impl Debug for SharedReference { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("SharedReference") - .field(&self.0) - .field(&self.1) - .finish() - } -} - -impl Serialize for SharedReference { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - if let SharedReference(Some(ty), arc) = self { - let value_type = registry::get_value_type(*ty); - if let Some(serializable) = value_type.any_as_serializable(arc) { - let mut t = serializer.serialize_tuple(2)?; - t.serialize_element(registry::get_value_type_global_name(*ty))?; - t.serialize_element(serializable)?; - t.end() - } else { - Err(serde::ser::Error::custom(format!( - "{:?} is not serializable", - arc - ))) - } - } else { - Err(serde::ser::Error::custom( - "untyped values are not serializable", - )) - } - } -} - -impl Display for SharedReference { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some(ty) = self.0 { - write!(f, "value of type {}", registry::get_value_type(ty).name) - } else { - write!(f, "untyped value") - } - } -} - -impl<'de> Deserialize<'de> for SharedReference { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - struct Visitor; - - impl<'de> serde::de::Visitor<'de> for Visitor { - type Value = SharedReference; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a serializable shared reference") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: serde::de::SeqAccess<'de>, - { - if let Some(global_name) = seq.next_element()? { - if let Some(ty) = registry::get_value_type_id_by_global_name(global_name) { - if let Some(seed) = registry::get_value_type(ty).get_any_deserialize_seed() - { - if let Some(value) = seq.next_element_seed(seed)? { - Ok(SharedReference::new(Some(ty), value.into())) - } else { - Err(serde::de::Error::invalid_length( - 1, - &"tuple with type and value", - )) - } - } else { - Err(serde::de::Error::custom(format!( - "{ty} is not deserializable" - ))) - } - } else { - Err(serde::de::Error::unknown_variant(global_name, &[])) - } - } else { - Err(serde::de::Error::invalid_length( - 0, - &"tuple with type and value", - )) - } - } - } - - deserializer.deserialize_tuple(2, Visitor) - } -} - -#[derive(Debug, Clone, PartialOrd, Ord)] -pub struct TransientSharedValue(pub Arc); - -impl TransientSharedValue { - pub fn downcast(self) -> Option> { - match Arc::downcast(self.0.magic_any_arc()) { - Ok(data) => Some(data), - Err(_) => None, - } - } -} - -impl Hash for TransientSharedValue { - fn hash(&self, state: &mut H) { - self.0.hash(state); - } -} - -impl PartialEq for TransientSharedValue { - #[allow(clippy::op_ref)] - fn eq(&self, other: &Self) -> bool { - &self.0 == &other.0 - } -} -impl Eq for TransientSharedValue {} -impl Serialize for TransientSharedValue { - fn serialize(&self, _serializer: S) -> Result - where - S: serde::Serializer, - { - Err(serde::ser::Error::custom( - "Transient values can't be serialized", - )) - } -} -impl<'de> Deserialize<'de> for TransientSharedValue { - fn deserialize(_deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - unreachable!("Transient values can't be serialized") - } -} - -#[derive(Debug, Clone, PartialOrd, Ord)] -pub struct SharedValue(pub Option, pub Arc); - -impl SharedValue { - pub fn downcast(self) -> Option> { - match Arc::downcast(self.1.magic_any_arc()) { - Ok(data) => Some(data), - Err(_) => None, - } - } -} - -impl PartialEq for SharedValue { - // this breaks without the ref - #[allow(clippy::op_ref)] - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 && &self.1 == &other.1 - } -} - -impl Eq for SharedValue {} - -impl Hash for SharedValue { - fn hash(&self, state: &mut H) { - self.0.hash(state); - self.1.hash(state); - } -} - -impl Display for SharedValue { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some(ty) = self.0 { - write!(f, "value of type {}", registry::get_value_type(ty).name) - } else { - write!(f, "untyped value") - } - } -} - -impl Serialize for SharedValue { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - if let SharedValue(Some(ty), arc) = self { - let value_type = registry::get_value_type(*ty); - if let Some(serializable) = value_type.magic_as_serializable(arc) { - let mut t = serializer.serialize_tuple(2)?; - t.serialize_element(registry::get_value_type_global_name(*ty))?; - t.serialize_element(serializable)?; - t.end() - } else { - Err(serde::ser::Error::custom(format!( - "{:?} is not serializable", - arc - ))) - } - } else { - Err(serde::ser::Error::custom( - "untyped values are not serializable", - )) - } - } -} - -impl<'de> Deserialize<'de> for SharedValue { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - struct Visitor; - - impl<'de> serde::de::Visitor<'de> for Visitor { - type Value = SharedValue; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a serializable shared value") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: serde::de::SeqAccess<'de>, - { - if let Some(global_name) = seq.next_element()? { - if let Some(ty) = registry::get_value_type_id_by_global_name(global_name) { - if let Some(seed) = - registry::get_value_type(ty).get_magic_deserialize_seed() - { - if let Some(value) = seq.next_element_seed(seed)? { - Ok(SharedValue(Some(ty), value.into())) - } else { - Err(serde::de::Error::invalid_length( - 1, - &"tuple with type and value", - )) - } - } else { - Err(serde::de::Error::custom(format!( - "{ty} is not deserializable" - ))) - } - } else { - Err(serde::de::Error::unknown_variant(global_name, &[])) - } - } else { - Err(serde::de::Error::invalid_length( - 0, - &"tuple with type and value", - )) - } - } - } - - deserializer.deserialize_tuple(2, Visitor) - } -} - -/// Intermediate representation of task inputs. -/// -/// When a task is called, all its arguments will be converted and stored as -/// [`ConcreteTaskInput`]s. When the task is actually run, these inputs will be -/// converted back into the argument types. This is handled by the -/// [`TaskInput`][crate::TaskInput] trait. -#[allow(clippy::derived_hash_with_manual_eq)] -#[derive(Debug, Hash, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)] -pub enum ConcreteTaskInput { - TaskOutput(TaskId), - TaskCell(TaskId, CellId), - List(Vec), - String(RcStr), - Bool(bool), - Usize(usize), - I8(i8), - U8(u8), - I16(i16), - U16(u16), - I32(i32), - U32(u32), - U64(u64), - #[default] - Nothing, - SharedValue(SharedValue), - TransientSharedValue(TransientSharedValue), - SharedReference(SharedReference), -} - -impl ConcreteTaskInput { - pub async fn resolve_to_value(self) -> Result { - let tt = turbo_tasks(); - let mut current = self; - loop { - current = match current { - ConcreteTaskInput::TaskOutput(task_id) => { - read_task_output(&*tt, task_id, false).await?.into() - } - ConcreteTaskInput::TaskCell(task_id, index) => { - read_task_cell(&*tt, task_id, index).await?.into() - } - _ => return Ok(current), - } - } - } - - pub async fn resolve(self) -> Result { - let tt = turbo_tasks(); - let mut current = self; - loop { - current = match current { - ConcreteTaskInput::TaskOutput(task_id) => { - read_task_output(&*tt, task_id, false).await?.into() - } - ConcreteTaskInput::List(list) => { - if list.iter().all(|i| i.is_resolved()) { - return Ok(ConcreteTaskInput::List(list)); - } - fn resolve_all( - list: Vec, - ) -> Pin>> + Send>> - { - use crate::TryJoinIterExt; - Box::pin(list.into_iter().map(|i| i.resolve()).try_join()) - } - return Ok(ConcreteTaskInput::List(resolve_all(list).await?)); - } - _ => return Ok(current), - } - } - } - - pub fn shrink_to_fit(&mut self) { - if let ConcreteTaskInput::List(list) = self { - list.shrink_to_fit(); - list.iter_mut().for_each(|i| i.shrink_to_fit()); - } - } - - pub fn get_task_id(&self) -> Option { - match self { - ConcreteTaskInput::TaskOutput(t) | ConcreteTaskInput::TaskCell(t, _) => Some(*t), - _ => None, - } - } - - pub fn is_resolved(&self) -> bool { - match self { - ConcreteTaskInput::TaskOutput(_) => false, - ConcreteTaskInput::List(list) => list.iter().all(|i| i.is_resolved()), - _ => true, - } - } - - pub fn is_nothing(&self) -> bool { - matches!(self, ConcreteTaskInput::Nothing) - } -} - -impl From for ConcreteTaskInput { - fn from(raw_vc: RawVc) -> Self { - match raw_vc { - RawVc::TaskOutput(task) => ConcreteTaskInput::TaskOutput(task), - RawVc::TaskCell(task, i) => ConcreteTaskInput::TaskCell(task, i), - } - } -} - -impl From for ConcreteTaskInput { - fn from(content: CellContent) -> Self { - match content { - CellContent(None) => ConcreteTaskInput::Nothing, - CellContent(Some(shared_ref)) => ConcreteTaskInput::SharedReference(shared_ref), - } - } -} - -impl Display for ConcreteTaskInput { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ConcreteTaskInput::TaskOutput(task) => write!(f, "task output {}", task), - ConcreteTaskInput::TaskCell(task, index) => write!(f, "cell {} in {}", index, task), - ConcreteTaskInput::List(list) => write!( - f, - "list {}", - list.iter() - .map(|i| i.to_string()) - .collect::>() - .join(", ") - ), - ConcreteTaskInput::String(s) => write!(f, "string {:?}", s), - ConcreteTaskInput::Bool(b) => write!(f, "bool {:?}", b), - ConcreteTaskInput::Usize(v) => write!(f, "usize {}", v), - ConcreteTaskInput::I8(v) => write!(f, "i8 {}", v), - ConcreteTaskInput::U8(v) => write!(f, "u8 {}", v), - ConcreteTaskInput::I16(v) => write!(f, "i16 {}", v), - ConcreteTaskInput::U16(v) => write!(f, "u16 {}", v), - ConcreteTaskInput::I32(v) => write!(f, "i32 {}", v), - ConcreteTaskInput::U32(v) => write!(f, "u32 {}", v), - ConcreteTaskInput::U64(v) => write!(f, "u64 {}", v), - ConcreteTaskInput::Nothing => write!(f, "nothing"), - ConcreteTaskInput::SharedValue(_) => write!(f, "any value"), - ConcreteTaskInput::TransientSharedValue(_) => write!(f, "any transient value"), - ConcreteTaskInput::SharedReference(data) => { - write!(f, "shared reference with {}", data) - } - } - } -} diff --git a/crates/turbo-tasks/src/task/function.rs b/crates/turbo-tasks/src/task/function.rs index 042218c6ae552..243404ca7490d 100644 --- a/crates/turbo-tasks/src/task/function.rs +++ b/crates/turbo-tasks/src/task/function.rs @@ -26,13 +26,12 @@ use std::{future::Future, marker::PhantomData, pin::Pin}; use anyhow::Result; use super::{TaskInput, TaskOutput}; -use crate::{ConcreteTaskInput, RawVc, Vc, VcRead, VcValueType}; +use crate::{magic_any::MagicAny, RawVc, Vc, VcRead, VcValueType}; pub type NativeTaskFuture = Pin> + Send>>; -pub type NativeTaskFn = Box NativeTaskFuture + Send + Sync>; pub trait TaskFn: Send + Sync + 'static { - fn functor(&self, this: Option, arg: &ConcreteTaskInput) -> Result; + fn functor(&self, this: Option, arg: &dyn MagicAny) -> Result; } pub trait IntoTaskFn { @@ -58,24 +57,26 @@ where } } -pub trait IntoTaskFnWithThis { +pub trait IntoTaskFnWithThis { type TaskFn: TaskFn; fn into_task_fn_with_this(self) -> Self::TaskFn; } -impl IntoTaskFnWithThis for F +impl IntoTaskFnWithThis for F where - F: TaskFnInputFunctionWithThis, + F: TaskFnInputFunctionWithThis, Mode: TaskFnMode, + This: Sync + Send + 'static, Inputs: TaskInputs, { - type TaskFn = FunctionTaskFnWithThis; + type TaskFn = FunctionTaskFnWithThis; fn into_task_fn_with_this(self) -> Self::TaskFn { FunctionTaskFnWithThis { task_fn: self, mode: PhantomData, + this: PhantomData, inputs: PhantomData, } } @@ -93,36 +94,46 @@ where Mode: TaskFnMode, Inputs: TaskInputs, { - fn functor(&self, this: Option, arg: &ConcreteTaskInput) -> Result { - TaskFnInputFunction::functor(&self.task_fn, this, arg) + fn functor(&self, _this: Option, arg: &dyn MagicAny) -> Result { + TaskFnInputFunction::functor(&self.task_fn, arg) } } -pub struct FunctionTaskFnWithThis { +pub struct FunctionTaskFnWithThis< + F, + Mode: TaskFnMode, + This: Sync + Send + 'static, + Inputs: TaskInputs, +> { task_fn: F, mode: PhantomData, + this: PhantomData, inputs: PhantomData, } -impl TaskFn for FunctionTaskFnWithThis +impl TaskFn for FunctionTaskFnWithThis where - F: TaskFnInputFunctionWithThis, + F: TaskFnInputFunctionWithThis, Mode: TaskFnMode, + This: Sync + Send + 'static, Inputs: TaskInputs, { - fn functor(&self, this: Option, arg: &ConcreteTaskInput) -> Result { + fn functor(&self, this: Option, arg: &dyn MagicAny) -> Result { + let Some(this) = this else { + panic!("Method needs a `self` argument"); + }; TaskFnInputFunctionWithThis::functor(&self.task_fn, this, arg) } } trait TaskFnInputFunction: Send + Sync + Clone + 'static { - fn functor(&self, this: Option, arg: &ConcreteTaskInput) -> Result; + fn functor(&self, arg: &dyn MagicAny) -> Result; } -trait TaskFnInputFunctionWithThis: +trait TaskFnInputFunctionWithThis: Send + Sync + Clone + 'static { - fn functor(&self, this: Option, arg: &ConcreteTaskInput) -> Result; + fn functor(&self, this: RawVc, arg: &dyn MagicAny) -> Result; } pub trait TaskInputs: Send + Sync + 'static {} @@ -155,6 +166,20 @@ macro_rules! task_inputs_impl { } } +fn get_args(arg: &dyn MagicAny) -> Result<&T> { + let value = arg.downcast_ref::(); + #[cfg(debug_assertions)] + return anyhow::Context::with_context(value, || { + format!( + "Invalid argument type, expected {} got {}", + std::any::type_name::(), + (*arg).magic_type_name() + ) + }); + #[cfg(not(debug_assertions))] + return anyhow::Context::context(value, "Invalid argument type"); +} + macro_rules! task_fn_impl { ( $async_fn_trait:ident $arg_len:literal $( $arg:ident )* ) => { impl TaskFnInputFunction for F @@ -164,21 +189,16 @@ macro_rules! task_fn_impl { Output: TaskOutput + 'static, { #[allow(non_snake_case)] - fn functor(&self, _this: Option, arg: &ConcreteTaskInput) -> Result { - #[allow(unused_parens)] - let ($($arg),*) = <($($arg),*) as TaskInput>::try_from_concrete(arg)?; - + fn functor(&self, arg: &dyn MagicAny) -> Result { let task_fn = self.clone(); - Ok(Box::new(move || { - let task_fn = task_fn.clone(); - $( - let $arg = $arg.clone(); - )* + let ($($arg,)*) = get_args::<($($arg,)*)>(arg)?; + $( + let $arg = $arg.clone(); + )* - Box::pin(async move { - Output::try_into_raw_vc((task_fn)($($arg),*)) - }) + Ok(Box::pin(async move { + Output::try_into_raw_vc((task_fn)($($arg,)*)) })) } } @@ -191,26 +211,21 @@ macro_rules! task_fn_impl { Output: TaskOutput + 'static, { #[allow(non_snake_case)] - fn functor(&self, _this: Option, arg: &ConcreteTaskInput) -> Result { - #[allow(unused_parens)] - let ($($arg),*) = <($($arg),*) as TaskInput>::try_from_concrete(arg)?; - + fn functor(&self, arg: &dyn MagicAny) -> Result { let task_fn = self.clone(); - Ok(Box::new(move || { - let task_fn = task_fn.clone(); - $( - let $arg = $arg.clone(); - )* + let ($($arg,)*) = get_args::<($($arg,)*)>(arg)?; + $( + let $arg = $arg.clone(); + )* - Box::pin(async move { - Output::try_into_raw_vc((task_fn)($($arg),*).await) - }) + Ok(Box::pin(async move { + Output::try_into_raw_vc((task_fn)($($arg,)*).await) })) } } - impl TaskFnInputFunctionWithThis, $($arg,)*)> for F + impl TaskFnInputFunctionWithThis for F where Recv: VcValueType, $($arg: TaskInput + 'static,)* @@ -218,52 +233,42 @@ macro_rules! task_fn_impl { Output: TaskOutput + 'static, { #[allow(non_snake_case)] - fn functor(&self, this: Option, arg: &ConcreteTaskInput) -> Result { + fn functor(&self, this: RawVc, arg: &dyn MagicAny) -> Result { let task_fn = self.clone(); - let recv = Vc::::from(this.expect("Method need to have a `self` argument")); - - #[allow(unused_parens)] - let ($($arg),*) = <($($arg),*) as TaskInput>::try_from_concrete(arg)?; - - Ok(Box::new(move || { - let task_fn = task_fn.clone(); - $( - let $arg = $arg.clone(); - )* - - Box::pin(async move { - let recv = recv.await?; - let recv = <::Read as VcRead>::target_to_value_ref(&*recv); - Output::try_into_raw_vc((task_fn)(recv, $($arg),*)) - }) + let recv = Vc::::from(this); + + let ($($arg,)*) = get_args::<($($arg,)*)>(arg)?; + $( + let $arg = $arg.clone(); + )* + + Ok(Box::pin(async move { + let recv = recv.await?; + let recv = <::Read as VcRead>::target_to_value_ref(&*recv); + Output::try_into_raw_vc((task_fn)(recv, $($arg,)*)) })) } } - impl TaskFnInputFunctionWithThis, $($arg,)*)> for F + impl TaskFnInputFunctionWithThis for F where - Recv: Send + 'static, + Recv: Sync + Send + 'static, $($arg: TaskInput + 'static,)* F: Fn(Vc, $($arg,)*) -> Output + Send + Sync + Clone + 'static, Output: TaskOutput + 'static, { #[allow(non_snake_case)] - fn functor(&self, this: Option, arg: &ConcreteTaskInput) -> Result { + fn functor(&self, this: RawVc, arg: &dyn MagicAny) -> Result { let task_fn = self.clone(); - let recv = Vc::::from(this.expect("Method need to have a `self` argument")); - - #[allow(unused_parens)] - let ($($arg),*) = <($($arg),*) as TaskInput>::try_from_concrete(arg)?; + let recv = Vc::::from(this); - Ok(Box::new(move || { - let task_fn = task_fn.clone(); - $( - let $arg = $arg.clone(); - )* + let ($($arg,)*) = get_args::<($($arg,)*)>(arg)?; + $( + let $arg = $arg.clone(); + )* - Box::pin(async move { - Output::try_into_raw_vc((task_fn)(recv, $($arg),*)) - }) + Ok(Box::pin(async move { + Output::try_into_raw_vc((task_fn)(recv, $($arg,)*)) })) } } @@ -283,58 +288,48 @@ macro_rules! task_fn_impl { type Output = Fut::Output; } - impl TaskFnInputFunctionWithThis, $($arg,)*)> for F + impl TaskFnInputFunctionWithThis for F where Recv: VcValueType, $($arg: TaskInput + 'static,)* F: for<'a> $async_fn_trait<&'a Recv, $($arg,)*> + Clone + Send + Sync + 'static, { #[allow(non_snake_case)] - fn functor(&self, this: Option, arg: &ConcreteTaskInput) -> Result { + fn functor(&self, this: RawVc, arg: &dyn MagicAny) -> Result { let task_fn = self.clone(); - let recv = Vc::::from(this.expect("Method need to have a `self` argument")); - - #[allow(unused_parens)] - let ($($arg),*) = <($($arg),*) as TaskInput>::try_from_concrete(arg)?; - - Ok(Box::new(move || { - let task_fn = task_fn.clone(); - $( - let $arg = $arg.clone(); - )* - - Box::pin(async move { - let recv = recv.await?; - let recv = <::Read as VcRead>::target_to_value_ref(&*recv); - >::Output::try_into_raw_vc((task_fn)(recv, $($arg),*).await) - }) + let recv = Vc::::from(this); + + let ($($arg,)*) = get_args::<($($arg,)*)>(arg)?; + $( + let $arg = $arg.clone(); + )* + + Ok(Box::pin(async move { + let recv = recv.await?; + let recv = <::Read as VcRead>::target_to_value_ref(&*recv); + >::Output::try_into_raw_vc((task_fn)(recv, $($arg,)*).await) })) } } - impl TaskFnInputFunctionWithThis, $($arg,)*)> for F + impl TaskFnInputFunctionWithThis for F where - Recv: Send + 'static, + Recv: Sync + Send + 'static, $($arg: TaskInput + 'static,)* F: $async_fn_trait, $($arg,)*> + Clone + Send + Sync + 'static, { #[allow(non_snake_case)] - fn functor(&self, this: Option, arg: &ConcreteTaskInput) -> Result { + fn functor(&self, this: RawVc, arg: &dyn MagicAny) -> Result { let task_fn = self.clone(); - let recv = Vc::::from(this.expect("Method need to have a `self` argument")); - - #[allow(unused_parens)] - let ($($arg),*) = <($($arg),*) as TaskInput>::try_from_concrete(arg)?; + let recv = Vc::::from(this); - Ok(Box::new(move || { - let task_fn = task_fn.clone(); - $( - let $arg = $arg.clone(); - )* + let ($($arg,)*) = get_args::<($($arg,)*)>(arg)?; + $( + let $arg = $arg.clone(); + )* - Box::pin(async move { - , $($arg,)*>>::Output::try_into_raw_vc((task_fn)(recv, $($arg),*).await) - }) + Ok(Box::pin(async move { + , $($arg,)*>>::Output::try_into_raw_vc((task_fn)(recv, $($arg,)*).await) })) } } @@ -354,10 +349,6 @@ task_fn_impl! { AsyncFn9 9 A1 A2 A3 A4 A5 A6 A7 A8 A9 } task_fn_impl! { AsyncFn10 10 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 } task_fn_impl! { AsyncFn11 11 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 } task_fn_impl! { AsyncFn12 12 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 } -task_fn_impl! { AsyncFn13 13 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 } -task_fn_impl! { AsyncFn14 14 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 } -task_fn_impl! { AsyncFn15 15 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15 } -task_fn_impl! { AsyncFn16 16 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15 A16 } // There needs to be one more implementation than task_fn_impl to account for // the receiver. @@ -375,10 +366,6 @@ task_inputs_impl! { A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 } task_inputs_impl! { A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 } task_inputs_impl! { A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 } task_inputs_impl! { A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 } -task_inputs_impl! { A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 } -task_inputs_impl! { A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15 } -task_inputs_impl! { A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15 A16 } -task_inputs_impl! { A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15 A16 A17 } #[cfg(test)] mod tests { diff --git a/crates/turbo-tasks/src/task/mod.rs b/crates/turbo-tasks/src/task/mod.rs index 2fa600f9a502a..963a3a74a99d1 100644 --- a/crates/turbo-tasks/src/task/mod.rs +++ b/crates/turbo-tasks/src/task/mod.rs @@ -1,9 +1,9 @@ -pub(crate) mod concrete_task_input; pub(crate) mod function; +pub(crate) mod shared_reference; pub(crate) mod task_input; pub(crate) mod task_output; -pub use concrete_task_input::ConcreteTaskInput; -pub use function::{AsyncFunctionMode, FunctionMode, IntoTaskFn, NativeTaskFn, TaskFn}; +pub use function::{AsyncFunctionMode, FunctionMode, IntoTaskFn, TaskFn}; +pub use shared_reference::SharedReference; pub use task_input::TaskInput; pub use task_output::TaskOutput; diff --git a/crates/turbo-tasks/src/task/shared_reference.rs b/crates/turbo-tasks/src/task/shared_reference.rs new file mode 100644 index 0000000000000..747f2477c98ca --- /dev/null +++ b/crates/turbo-tasks/src/task/shared_reference.rs @@ -0,0 +1,146 @@ +use std::{ + any::Any, + fmt::{Debug, Display}, + hash::Hash, +}; + +use anyhow::Result; +use serde::{ser::SerializeTuple, Deserialize, Serialize}; +use unsize::CoerceUnsize; + +use crate::{ + registry, + triomphe_utils::{coerce_to_any_send_sync, downcast_triomphe_arc}, + ValueTypeId, +}; + +/// A type-erased wrapper for [`triomphe::Arc`]. +#[derive(Clone)] +pub struct SharedReference( + pub Option, + pub triomphe::Arc, +); + +impl SharedReference { + pub fn new(type_id: Option, data: triomphe::Arc) -> Self { + Self(type_id, data.unsize(coerce_to_any_send_sync())) + } + + pub fn downcast(self) -> Result, Self> { + match downcast_triomphe_arc(self.1) { + Ok(data) => Ok(data), + Err(data) => Err(Self(self.0, data)), + } + } +} + +impl Hash for SharedReference { + fn hash(&self, state: &mut H) { + Hash::hash(&(&*self.1 as *const (dyn Any + Send + Sync)), state) + } +} +impl PartialEq for SharedReference { + // Must compare with PartialEq rather than std::ptr::addr_eq since the latter + // only compares their addresses. + #[allow(ambiguous_wide_pointer_comparisons)] + fn eq(&self, other: &Self) -> bool { + triomphe::Arc::ptr_eq(&self.1, &other.1) + } +} +impl Eq for SharedReference {} + +impl Debug for SharedReference { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("SharedReference") + .field(&self.0) + .field(&self.1) + .finish() + } +} + +impl Serialize for SharedReference { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + if let SharedReference(Some(ty), arc) = self { + let value_type = registry::get_value_type(*ty); + if let Some(serializable) = value_type.any_as_serializable(arc) { + let mut t = serializer.serialize_tuple(2)?; + t.serialize_element(registry::get_value_type_global_name(*ty))?; + t.serialize_element(serializable)?; + t.end() + } else { + Err(serde::ser::Error::custom(format!( + "{:?} is not serializable", + arc + ))) + } + } else { + Err(serde::ser::Error::custom( + "untyped values are not serializable", + )) + } + } +} + +impl Display for SharedReference { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(ty) = self.0 { + write!(f, "value of type {}", registry::get_value_type(ty).name) + } else { + write!(f, "untyped value") + } + } +} + +impl<'de> Deserialize<'de> for SharedReference { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = SharedReference; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a serializable shared reference") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + if let Some(global_name) = seq.next_element()? { + if let Some(ty) = registry::get_value_type_id_by_global_name(global_name) { + if let Some(seed) = registry::get_value_type(ty).get_any_deserialize_seed() + { + if let Some(value) = seq.next_element_seed(seed)? { + Ok(SharedReference::new(Some(ty), value.into())) + } else { + Err(serde::de::Error::invalid_length( + 1, + &"tuple with type and value", + )) + } + } else { + Err(serde::de::Error::custom(format!( + "{ty} is not deserializable" + ))) + } + } else { + Err(serde::de::Error::unknown_variant(global_name, &[])) + } + } else { + Err(serde::de::Error::invalid_length( + 0, + &"tuple with type and value", + )) + } + } + } + + deserializer.deserialize_tuple(2, Visitor) + } +} diff --git a/crates/turbo-tasks/src/task/task_input.rs b/crates/turbo-tasks/src/task/task_input.rs index 9d21f9e228c55..ef7cd47b5fe3c 100644 --- a/crates/turbo-tasks/src/task/task_input.rs +++ b/crates/turbo-tasks/src/task/task_input.rs @@ -1,231 +1,131 @@ -use std::{ - any::{type_name, Any}, - marker::PhantomData, - sync::Arc, -}; +use std::{any::Any, fmt::Debug, hash::Hash}; -use anyhow::{anyhow, bail, Result}; +use anyhow::Result; +use async_trait::async_trait; +use serde::{Deserialize, Serialize}; -use super::concrete_task_input::TransientSharedValue; use crate::{ - magic_any::MagicAny, ConcreteTaskInput, RawVc, RcStr, SharedValue, TaskId, TransientInstance, - TransientValue, TypedForInput, Value, ValueTypeId, Vc, VcValueType, + magic_any::MagicAny, vc::ResolvedVc, RcStr, TaskId, TransientInstance, TransientValue, Value, + ValueTypeId, Vc, }; /// Trait to implement in order for a type to be accepted as a /// [`#[turbo_tasks::function]`][crate::function] argument. /// /// See also [`ConcreteTaskInput`]. -pub trait TaskInput: Send + Sync + Clone { - fn try_from_concrete(input: &ConcreteTaskInput) -> Result; - fn into_concrete(self) -> ConcreteTaskInput; -} - -impl TaskInput for ConcreteTaskInput { - fn try_from_concrete(input: &ConcreteTaskInput) -> Result { - Ok(input.clone()) +#[async_trait] +pub trait TaskInput: Send + Sync + Clone + Debug + PartialEq + Eq + Hash { + async fn resolve(&self) -> Result { + Ok(self.clone()) } - - fn into_concrete(self) -> ConcreteTaskInput { - self + fn is_resolved(&self) -> bool { + true } -} - -impl TaskInput for RcStr { - fn try_from_concrete(input: &ConcreteTaskInput) -> Result { - match input { - ConcreteTaskInput::String(s) => Ok(s.clone()), - _ => bail!("invalid task input type, expected String"), - } - } - - fn into_concrete(self) -> ConcreteTaskInput { - ConcreteTaskInput::String(self) + fn is_transient(&self) -> bool { + false } } -impl TaskInput for bool { - fn try_from_concrete(input: &ConcreteTaskInput) -> Result { - match input { - ConcreteTaskInput::Bool(b) => Ok(*b), - _ => bail!("invalid task input type, expected Bool"), - } - } +macro_rules! impl_task_input { + ($($t:ty),*) => { + $( + #[async_trait] + impl TaskInput for $t {} + )* + }; +} - fn into_concrete(self) -> ConcreteTaskInput { - ConcreteTaskInput::Bool(self) - } +impl_task_input! { + (), + bool, + u8, + u16, + u32, + i32, + u64, + usize, + RcStr, + TaskId, + ValueTypeId } +#[async_trait] impl TaskInput for Vec where T: TaskInput, { - fn try_from_concrete(value: &ConcreteTaskInput) -> Result { - match value { - ConcreteTaskInput::List(list) => Ok(list - .iter() - .map(|i| ::try_from_concrete(i)) - .collect::, _>>()?), - _ => bail!("invalid task input type, expected List"), - } + fn is_resolved(&self) -> bool { + self.iter().all(TaskInput::is_resolved) } - fn into_concrete(self) -> ConcreteTaskInput { - ConcreteTaskInput::List( - self.into_iter() - .map(|i| ::into_concrete(i)) - .collect::>(), - ) + fn is_transient(&self) -> bool { + self.iter().any(TaskInput::is_transient) } -} - -impl TaskInput for u8 { - fn try_from_concrete(value: &ConcreteTaskInput) -> Result { - match value { - ConcreteTaskInput::U8(value) => Ok(*value), - _ => bail!("invalid task input type, expected U8"), - } - } - - fn into_concrete(self) -> ConcreteTaskInput { - ConcreteTaskInput::U8(self) - } -} -impl TaskInput for u16 { - fn try_from_concrete(value: &ConcreteTaskInput) -> Result { - match value { - ConcreteTaskInput::U16(value) => Ok(*value), - _ => bail!("invalid task input type, expected U16"), + async fn resolve(&self) -> Result { + let mut resolved = Vec::with_capacity(self.len()); + for value in self { + resolved.push(value.resolve().await?); } - } - - fn into_concrete(self) -> ConcreteTaskInput { - ConcreteTaskInput::U16(self) + Ok(resolved) } } -impl TaskInput for u32 { - fn try_from_concrete(value: &ConcreteTaskInput) -> Result { - match value { - ConcreteTaskInput::U32(value) => Ok(*value), - _ => bail!("invalid task input type, expected U32"), - } - } - - fn into_concrete(self) -> ConcreteTaskInput { - ConcreteTaskInput::U32(self) - } -} - -impl TaskInput for i32 { - fn try_from_concrete(value: &ConcreteTaskInput) -> Result { - match value { - ConcreteTaskInput::I32(value) => Ok(*value), - _ => bail!("invalid task input type, expected I32"), - } - } - - fn into_concrete(self) -> ConcreteTaskInput { - ConcreteTaskInput::I32(self) - } -} - -impl TaskInput for u64 { - fn try_from_concrete(value: &ConcreteTaskInput) -> Result { - match value { - ConcreteTaskInput::U64(value) => Ok(*value), - _ => bail!("invalid task input type, expected U64"), - } - } - - fn into_concrete(self) -> ConcreteTaskInput { - ConcreteTaskInput::U64(self) - } -} - -impl TaskInput for usize { - fn try_from_concrete(value: &ConcreteTaskInput) -> Result { - match value { - ConcreteTaskInput::Usize(value) => Ok(*value), - _ => bail!("invalid task input type, expected Usize"), +#[async_trait] +impl TaskInput for Option +where + T: TaskInput, +{ + fn is_resolved(&self) -> bool { + match self { + Some(value) => value.is_resolved(), + None => true, } } - fn into_concrete(self) -> ConcreteTaskInput { - ConcreteTaskInput::Usize(self) - } -} - -impl TaskInput for ValueTypeId { - fn try_from_concrete(value: &ConcreteTaskInput) -> Result { - match value { - ConcreteTaskInput::U32(value) => Ok(ValueTypeId::from(*value)), - _ => bail!("invalid task input type, expected ValueTypeId"), + fn is_transient(&self) -> bool { + match self { + Some(value) => value.is_transient(), + None => false, } } - fn into_concrete(self) -> ConcreteTaskInput { - ConcreteTaskInput::U32(*self) - } -} - -impl TaskInput for TaskId { - fn try_from_concrete(value: &ConcreteTaskInput) -> Result { - match value { - ConcreteTaskInput::U32(value) => Ok(TaskId::from(*value)), - _ => bail!("invalid task input type, expected TaskId"), + async fn resolve(&self) -> Result { + match self { + Some(value) => Ok(Some(value.resolve().await?)), + None => Ok(None), } } - - fn into_concrete(self) -> ConcreteTaskInput { - ConcreteTaskInput::U32(*self) - } } -impl TaskInput for Option +#[async_trait] +impl TaskInput for Vc where - T: TaskInput, + T: Send, { - fn try_from_concrete(value: &ConcreteTaskInput) -> Result { - match value { - ConcreteTaskInput::Nothing => Ok(None), - _ => Ok(Some(::try_from_concrete(value)?)), - } + fn is_resolved(&self) -> bool { + Vc::is_resolved(*self) } - fn into_concrete(self) -> ConcreteTaskInput { - match self { - None => ConcreteTaskInput::Nothing, - Some(value) => ::into_concrete(value), - } + fn is_transient(&self) -> bool { + false + } + + async fn resolve(&self) -> Result { + Vc::resolve(*self).await } } -impl TaskInput for Vc +impl TaskInput for ResolvedVc where T: Send, { - fn try_from_concrete(input: &ConcreteTaskInput) -> Result { - match input { - ConcreteTaskInput::TaskCell(task, index) => Ok(Vc { - node: RawVc::TaskCell(*task, *index), - _t: PhantomData, - }), - ConcreteTaskInput::TaskOutput(task) => Ok(Vc { - node: RawVc::TaskOutput(*task), - _t: PhantomData, - }), - _ => bail!("invalid task input type, expected RawVc"), - } + fn is_resolved(&self) -> bool { + true } - fn into_concrete(self) -> ConcreteTaskInput { - match self.node { - RawVc::TaskCell(task, index) => ConcreteTaskInput::TaskCell(task, index), - RawVc::TaskOutput(task) => ConcreteTaskInput::TaskOutput(task), - } + fn is_transient(&self) -> bool { + false } } @@ -236,127 +136,111 @@ where + Clone + std::hash::Hash + Eq - + Ord + Send + Sync - + VcValueType - + TypedForInput + + Serialize + + for<'de> Deserialize<'de> + 'static, { - fn try_from_concrete(input: &ConcreteTaskInput) -> Result { - match input { - ConcreteTaskInput::SharedValue(value) => { - let v = value.1.downcast_ref::().ok_or_else(|| { - anyhow!( - "invalid task input type, expected {} got {:?}", - type_name::(), - value.1, - ) - })?; - Ok(Value::new(v.clone())) - } - _ => bail!("invalid task input type, expected {}", type_name::()), - } + fn is_resolved(&self) -> bool { + true } - fn into_concrete(self) -> ConcreteTaskInput { - let raw_value: T = self.into_value(); - ConcreteTaskInput::SharedValue(SharedValue( - Some(T::get_value_type_id()), - Arc::new(raw_value), - )) + fn is_transient(&self) -> bool { + false } } impl TaskInput for TransientValue where - T: MagicAny + Clone + 'static, + T: MagicAny + Clone + Debug + Hash + Eq + 'static, { - fn try_from_concrete(input: &ConcreteTaskInput) -> Result { - match input { - ConcreteTaskInput::TransientSharedValue(value) => { - let v = value.0.downcast_ref::().ok_or_else(|| { - anyhow!( - "invalid task input type, expected {} got {:?}", - type_name::(), - value.0, - ) - })?; - Ok(TransientValue::new(v.clone())) - } - _ => bail!("invalid task input type, expected {}", type_name::()), - } + fn is_transient(&self) -> bool { + true } +} - fn into_concrete(self) -> ConcreteTaskInput { - let raw_value: T = self.into_value(); - ConcreteTaskInput::TransientSharedValue(TransientSharedValue(Arc::new(raw_value))) +impl Serialize for TransientValue +where + T: MagicAny + Clone + 'static, +{ + fn serialize(&self, _serializer: S) -> Result + where + S: serde::Serializer, + { + Err(serde::ser::Error::custom( + "cannot serialize transient task inputs", + )) } } -impl TaskInput for TransientInstance +impl<'de, T> Deserialize<'de> for TransientValue where - T: Send + Sync + 'static, + T: MagicAny + Clone + 'static, { - fn try_from_concrete(input: &ConcreteTaskInput) -> Result { - match input { - ConcreteTaskInput::SharedReference(reference) => { - if let Ok(i) = reference.clone().try_into() { - Ok(i) - } else { - bail!( - "invalid task input type, expected {} got {:?}", - type_name::(), - reference.0, - ) - } - } - _ => bail!("invalid task input type, expected {}", type_name::()), - } + fn deserialize(_deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Err(serde::de::Error::custom( + "cannot deserialize transient task inputs", + )) } +} - fn into_concrete(self) -> ConcreteTaskInput { - ConcreteTaskInput::SharedReference(self.into()) +impl TaskInput for TransientInstance +where + T: Sync + Send, +{ + fn is_transient(&self) -> bool { + true } } -impl TaskInput for () { - fn try_from_concrete(input: &ConcreteTaskInput) -> Result { - match input { - ConcreteTaskInput::Nothing => Ok(()), - _ => bail!("invalid task input type, expected Nothing"), - } +impl Serialize for TransientInstance { + fn serialize(&self, _serializer: S) -> Result + where + S: serde::Serializer, + { + Err(serde::ser::Error::custom( + "cannot serialize transient task inputs", + )) } +} - fn into_concrete(self) -> ConcreteTaskInput { - ConcreteTaskInput::Nothing +impl<'de, T> Deserialize<'de> for TransientInstance { + fn deserialize(_deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Err(serde::de::Error::custom( + "cannot deserialize transient task inputs", + )) } } macro_rules! tuple_impls { ( $( $name:ident )+ ) => { + #[async_trait] impl<$($name: TaskInput),+> TaskInput for ($($name,)+) + where $($name: TaskInput),+ { #[allow(non_snake_case)] - fn try_from_concrete(input: &ConcreteTaskInput) -> Result { - match input { - ConcreteTaskInput::List(value) => { - let mut iter = value.iter(); - $( - let $name = iter.next().ok_or_else(|| anyhow!("missing tuple element"))?; - let $name = TaskInput::try_from_concrete($name)?; - )+ - Ok(($($name,)+)) - } - _ => bail!("invalid task input type {input:?}, expected tuple {}", stringify!(($($name,)+))), - } + fn is_resolved(&self) -> bool { + let ($($name,)+) = self; + $($name.is_resolved() &&)+ true + } + + #[allow(non_snake_case)] + fn is_transient(&self) -> bool { + let ($($name,)+) = self; + $($name.is_transient() ||)+ false } #[allow(non_snake_case)] - fn into_concrete(self) -> ConcreteTaskInput { + async fn resolve(&self) -> Result { let ($($name,)+) = self; - let ($($name,)+) = ($($name.into_concrete(),)+); - ConcreteTaskInput::List(vec![ $($name,)+ ]) + Ok(($($name.resolve().await?,)+)) } } }; @@ -375,10 +259,6 @@ tuple_impls! { A B C D E F G H I } tuple_impls! { A B C D E F G H I J } tuple_impls! { A B C D E F G H I J K } tuple_impls! { A B C D E F G H I J K L } -tuple_impls! { A B C D E F G H I J K L M } -tuple_impls! { A B C D E F G H I J K L M N } -tuple_impls! { A B C D E F G H I J K L M N O } -tuple_impls! { A B C D E F G H I J K L M N O P} #[cfg(test)] mod tests { @@ -389,115 +269,99 @@ mod tests { // the crate name directly. use crate as turbo_tasks; - fn conversion(t: T) -> Result + fn assert_task_input(_: T) where T: TaskInput, { - TaskInput::try_from_concrete(&TaskInput::into_concrete(t)) - } - - macro_rules! test_conversion { - ($input:expr) => { - assert_eq!(conversion($input)?, $input); - }; } #[test] fn test_no_fields() -> Result<()> { - #[derive(Clone, TaskInput, Eq, PartialEq, Debug)] + #[derive(Clone, TaskInput, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] struct NoFields; - test_conversion!(NoFields); + assert_task_input(NoFields); Ok(()) } #[test] fn test_one_unnamed_field() -> Result<()> { - #[derive(Clone, TaskInput, Eq, PartialEq, Debug)] + #[derive(Clone, TaskInput, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] struct OneUnnamedField(u32); - test_conversion!(OneUnnamedField(42)); + assert_task_input(OneUnnamedField(42)); Ok(()) } #[test] fn test_multiple_unnamed_fields() -> Result<()> { - #[derive(Clone, TaskInput, Eq, PartialEq, Debug)] + #[derive(Clone, TaskInput, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] struct MultipleUnnamedFields(u32, RcStr); - test_conversion!(MultipleUnnamedFields(42, "42".into())); + assert_task_input(MultipleUnnamedFields(42, "42".into())); Ok(()) } #[test] fn test_one_named_field() -> Result<()> { - #[derive(Clone, TaskInput, Eq, PartialEq, Debug)] + #[derive(Clone, TaskInput, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] struct OneNamedField { named: u32, } - test_conversion!(OneNamedField { named: 42 }); + assert_task_input(OneNamedField { named: 42 }); Ok(()) } #[test] fn test_multiple_named_fields() -> Result<()> { - #[derive(Clone, TaskInput, Eq, PartialEq, Debug)] + #[derive(Clone, TaskInput, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] struct MultipleNamedFields { named: u32, other: RcStr, } - test_conversion!(MultipleNamedFields { + assert_task_input(MultipleNamedFields { named: 42, - other: "42".into() + other: "42".into(), }); Ok(()) } #[test] fn test_generic_field() -> Result<()> { - #[derive(Clone, TaskInput, Eq, PartialEq, Debug)] + #[derive(Clone, TaskInput, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] struct GenericField(T); - test_conversion!(GenericField(42)); - test_conversion!(GenericField(RcStr::from("42"))); + assert_task_input(GenericField(42)); + assert_task_input(GenericField(RcStr::from("42"))); Ok(()) } - #[test] - fn test_no_variant() -> Result<()> { - // This can't actually be tested at runtime because such an enum can't be - // constructed. However, the macro expansion is tested. - #[derive(Clone, TaskInput)] - enum NoVariants {} - Ok(()) - } - - #[derive(Clone, TaskInput, Eq, PartialEq, Debug)] + #[derive(Clone, TaskInput, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] enum OneVariant { Variant, } #[test] fn test_one_variant() -> Result<()> { - test_conversion!(OneVariant::Variant); + assert_task_input(OneVariant::Variant); Ok(()) } #[test] fn test_multiple_variants() -> Result<()> { - #[derive(Clone, TaskInput, PartialEq, Eq, Debug)] + #[derive(Clone, TaskInput, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] enum MultipleVariants { Variant1, Variant2, } - test_conversion!(MultipleVariants::Variant2); + assert_task_input(MultipleVariants::Variant2); Ok(()) } - #[derive(Clone, TaskInput, Eq, PartialEq, Debug)] + #[derive(Clone, TaskInput, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] enum MultipleVariantsAndHeterogeneousFields { Variant1, Variant2(u32), @@ -508,16 +372,16 @@ mod tests { #[test] fn test_multiple_variants_and_heterogeneous_fields() -> Result<()> { - test_conversion!(MultipleVariantsAndHeterogeneousFields::Variant5 { + assert_task_input(MultipleVariantsAndHeterogeneousFields::Variant5 { named: 42, - other: "42".into() + other: "42".into(), }); Ok(()) } #[test] fn test_nested_variants() -> Result<()> { - #[derive(Clone, TaskInput, Eq, PartialEq, Debug)] + #[derive(Clone, TaskInput, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] enum NestedVariants { Variant1, Variant2(MultipleVariantsAndHeterogeneousFields), @@ -526,15 +390,15 @@ mod tests { Variant5 { named: OneVariant, other: RcStr }, } - test_conversion!(NestedVariants::Variant5 { + assert_task_input(NestedVariants::Variant5 { named: OneVariant::Variant, - other: "42".into() + other: "42".into(), }); - test_conversion!(NestedVariants::Variant2( + assert_task_input(NestedVariants::Variant2( MultipleVariantsAndHeterogeneousFields::Variant5 { named: 42, - other: "42".into() - } + other: "42".into(), + }, )); Ok(()) } diff --git a/crates/turbo-tasks/src/trait_helpers.rs b/crates/turbo-tasks/src/trait_helpers.rs index 370443acac31e..d8467e1e7cfaf 100644 --- a/crates/turbo-tasks/src/trait_helpers.rs +++ b/crates/turbo-tasks/src/trait_helpers.rs @@ -11,10 +11,11 @@ pub fn get_trait_method( if let Some(func) = registry::get_value_type(value_type).get_trait_method(&key) { Ok(*func) } else if let Some(func) = registry::get_trait(trait_type) - .default_trait_methods + .methods .get(&key.1) + .and_then(|method| method.default_method) { - Ok(*func) + Ok(func) } else { Err(key.1) } diff --git a/crates/turbo-tasks/src/trait_ref.rs b/crates/turbo-tasks/src/trait_ref.rs index a7784cbef624c..d5eb54ecd8376 100644 --- a/crates/turbo-tasks/src/trait_ref.rs +++ b/crates/turbo-tasks/src/trait_ref.rs @@ -48,18 +48,6 @@ impl PartialEq for TraitRef { impl Eq for TraitRef {} -impl PartialOrd for TraitRef { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for TraitRef { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.shared_reference.cmp(&other.shared_reference) - } -} - impl std::hash::Hash for TraitRef { fn hash(&self, state: &mut H) { self.shared_reference.hash(state) diff --git a/crates/turbo-tasks/src/value.rs b/crates/turbo-tasks/src/value.rs index 0363b09c682d1..b3b36db17840f 100644 --- a/crates/turbo-tasks/src/value.rs +++ b/crates/turbo-tasks/src/value.rs @@ -1,11 +1,13 @@ use std::{fmt::Debug, marker::PhantomData, ops::Deref}; +use serde::{Deserialize, Serialize}; + use crate::SharedReference; /// Pass a value by value (`Value`) instead of by reference (`Vc`). /// /// Persistent, requires serialization. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)] +#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)] pub struct Value { inner: T, } @@ -69,12 +71,17 @@ impl Deref for TransientValue { /// /// Doesn't require serialization, and won't be stored in the persistent cache /// in the future. -#[derive(Debug)] pub struct TransientInstance { inner: SharedReference, phantom: PhantomData, } +impl Debug for TransientInstance { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("TransientInstance").finish() + } +} + impl Clone for TransientInstance { fn clone(&self) -> Self { Self { @@ -98,18 +105,6 @@ impl std::hash::Hash for TransientInstance { } } -impl PartialOrd for TransientInstance { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for TransientInstance { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.inner.cmp(&other.inner) - } -} - impl From> for triomphe::Arc { fn from(instance: TransientInstance) -> Self { // we know this downcast must work because we have type T diff --git a/crates/turbo-tasks/src/value_type.rs b/crates/turbo-tasks/src/value_type.rs index 4d0301aacc2a1..d9d6e0246d8c2 100644 --- a/crates/turbo-tasks/src/value_type.rs +++ b/crates/turbo-tasks/src/value_type.rs @@ -14,7 +14,7 @@ use tracing::Span; use crate::{ id::{FunctionId, TraitTypeId}, - magic_any::{AnyDeserializeSeed, MagicAny, MagicAnyDeserializeSeed}, + magic_any::{AnyDeserializeSeed, MagicAny, MagicAnyDeserializeSeed, MagicAnySerializeSeed}, registry::{register_trait_type, register_value_type}, }; @@ -57,17 +57,6 @@ impl PartialEq for ValueType { } } -impl PartialOrd for ValueType { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl Ord for ValueType { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - (self as *const ValueType).cmp(&(other as *const ValueType)) - } -} - impl Debug for ValueType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut d = f.debug_struct("ValueType"); @@ -111,7 +100,7 @@ impl ValueType { /// This is internally used by `#[turbo_tasks::value]` pub fn new_with_magic_serialization< - T: Debug + Eq + Ord + Hash + Serialize + for<'de> Deserialize<'de> + Send + Sync + 'static, + T: Debug + Eq + Hash + Serialize + for<'de> Deserialize<'de> + Send + Sync + 'static, >() -> Self { Self { name: std::any::type_name::().to_string(), @@ -204,10 +193,24 @@ impl ValueType { } } +pub struct TraitMethod { + pub default_method: Option, + pub arg_serializer: MagicAnySerializeSeed, + pub arg_deserializer: MagicAnyDeserializeSeed, +} + +impl Debug for TraitMethod { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("TraitMethod") + .field("default_method", &self.default_method) + .finish() + } +} + #[derive(Debug)] pub struct TraitType { pub name: String, - pub(crate) default_trait_methods: AutoMap, FunctionId>, + pub(crate) methods: AutoMap, TraitMethod>, } impl Hash for TraitType { @@ -230,32 +233,43 @@ impl PartialEq for TraitType { } } -impl PartialOrd for TraitType { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for TraitType { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - (self as *const TraitType).cmp(&(other as *const TraitType)) - } -} - impl TraitType { pub fn new(name: String) -> Self { Self { name, - default_trait_methods: AutoMap::new(), + methods: AutoMap::new(), } } - pub fn register_default_trait_method( + pub fn register_trait_method(&mut self, name: Cow<'static, str>) + where + T: Serialize + for<'de> Deserialize<'de> + Debug + Eq + Hash + Send + Sync + 'static, + { + self.methods.insert( + name, + TraitMethod { + default_method: None, + arg_serializer: MagicAnySerializeSeed::new::(), + arg_deserializer: MagicAnyDeserializeSeed::new::(), + }, + ); + } + + pub fn register_default_trait_method( &mut self, name: Cow<'static, str>, native_fn: FunctionId, - ) { - self.default_trait_methods.insert(name, native_fn); + ) where + T: Serialize + for<'de> Deserialize<'de> + Debug + Eq + Hash + Send + Sync + 'static, + { + self.methods.insert( + name, + TraitMethod { + default_method: Some(native_fn), + arg_serializer: MagicAnySerializeSeed::new::(), + arg_deserializer: MagicAnyDeserializeSeed::new::(), + }, + ); } pub fn register(&'static self, global_name: &'static str) { @@ -269,5 +283,3 @@ impl TraitType { ) } } - -pub trait TraitMethod: Any {} diff --git a/crates/turbo-tasks/src/vc/mod.rs b/crates/turbo-tasks/src/vc/mod.rs index 1e11c402d7fab..19f106eb545d8 100644 --- a/crates/turbo-tasks/src/vc/mod.rs +++ b/crates/turbo-tasks/src/vc/mod.rs @@ -232,24 +232,6 @@ where impl Eq for Vc where T: ?Sized + Send {} -impl PartialOrd> for Vc -where - T: ?Sized + Send, -{ - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Vc -where - T: ?Sized + Send, -{ - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.node.cmp(&other.node) - } -} - impl Serialize for Vc where T: ?Sized + Send, @@ -386,6 +368,13 @@ where }) } + /// Returns `true` if the reference is resolved. + /// + /// See also [`Vc::resolve`]. + pub fn is_resolved(self) -> bool { + self.node.is_resolved() + } + /// Resolve the reference until it points to a cell directly in a strongly /// consistent way. /// diff --git a/crates/turbo-tasks/src/vc/resolved.rs b/crates/turbo-tasks/src/vc/resolved.rs index 7a80758d48640..8bdc40f648e70 100644 --- a/crates/turbo-tasks/src/vc/resolved.rs +++ b/crates/turbo-tasks/src/vc/resolved.rs @@ -20,7 +20,6 @@ use indexmap::{IndexMap, IndexSet}; use crate::{vc::Vc, RcStr}; -#[derive(Copy, Clone)] pub struct ResolvedVc where T: ?Sized + Send, @@ -28,6 +27,17 @@ where pub(crate) node: Vc, } +impl Copy for ResolvedVc where T: ?Sized + Send {} + +impl Clone for ResolvedVc +where + T: ?Sized + Send, +{ + fn clone(&self) -> Self { + *self + } +} + impl Deref for ResolvedVc where T: ?Sized + Send, @@ -39,6 +49,17 @@ where } } +impl PartialEq> for ResolvedVc +where + T: ?Sized + Send, +{ + fn eq(&self, other: &Self) -> bool { + self.node == other.node + } +} + +impl Eq for ResolvedVc where T: ?Sized + Send {} + impl Hash for ResolvedVc where T: ?Sized + Send, @@ -48,6 +69,17 @@ where } } +impl std::fmt::Debug for ResolvedVc +where + T: Send, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ResolvedVc") + .field("node", &self.node.node) + .finish() + } +} + /// Indicates that a type does not contain any instances of [`Vc`]. It may /// contain [`ResolvedVc`]. /// diff --git a/crates/turbopack-browser/src/chunking_context.rs b/crates/turbopack-browser/src/chunking_context.rs index 9e56f7a4d0561..616a30e01590c 100644 --- a/crates/turbopack-browser/src/chunking_context.rs +++ b/crates/turbopack-browser/src/chunking_context.rs @@ -88,7 +88,7 @@ impl BrowserChunkingContextBuilder { /// It splits "node_modules" separately as these are less likely to change /// during development #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(Debug, Clone, Hash, PartialOrd, Ord)] +#[derive(Debug, Clone, Hash)] pub struct BrowserChunkingContext { name: Option, /// This path get stripped off of chunk paths before generating output asset diff --git a/crates/turbopack-browser/src/ecmascript/list/asset.rs b/crates/turbopack-browser/src/ecmascript/list/asset.rs index 88deb66bf9055..bc0cc39046feb 100644 --- a/crates/turbopack-browser/src/ecmascript/list/asset.rs +++ b/crates/turbopack-browser/src/ecmascript/list/asset.rs @@ -134,7 +134,7 @@ impl Asset for EcmascriptDevChunkList { } } -#[derive(Debug, Clone, Copy, Ord, PartialOrd, Hash)] +#[derive(Debug, Clone, Copy, Hash)] #[turbo_tasks::value(serialization = "auto_for_input")] #[serde(rename_all = "camelCase")] pub enum EcmascriptDevChunkListSource { diff --git a/crates/turbopack-core/src/chunk/availability_info.rs b/crates/turbopack-core/src/chunk/availability_info.rs index 52637cab666a0..2657b814502f5 100644 --- a/crates/turbopack-core/src/chunk/availability_info.rs +++ b/crates/turbopack-core/src/chunk/availability_info.rs @@ -4,7 +4,7 @@ use turbo_tasks::Vc; use super::available_chunk_items::{AvailableChunkItemInfoMap, AvailableChunkItems}; #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(PartialOrd, Ord, Hash, Clone, Copy, Debug)] +#[derive(Hash, Clone, Copy, Debug)] pub enum AvailabilityInfo { /// Availability of modules is not tracked Untracked, diff --git a/crates/turbopack-core/src/chunk/mod.rs b/crates/turbopack-core/src/chunk/mod.rs index ffdffc6ce8d17..bfc69139cb57e 100644 --- a/crates/turbopack-core/src/chunk/mod.rs +++ b/crates/turbopack-core/src/chunk/mod.rs @@ -244,7 +244,7 @@ enum ChunkContentGraphNode { }, } -#[derive(Debug, Clone, Copy, TaskInput)] +#[derive(Debug, Clone, Copy, TaskInput, PartialEq, Eq, Hash, Serialize, Deserialize)] enum ChunkGraphNodeToReferences { PassthroughChunkItem(Vc>), ChunkItem(Vc>), diff --git a/crates/turbopack-core/src/compile_time_info.rs b/crates/turbopack-core/src/compile_time_info.rs index e33d4006b604b..fc4ef30178bc2 100644 --- a/crates/turbopack-core/src/compile_time_info.rs +++ b/crates/turbopack-core/src/compile_time_info.rs @@ -75,7 +75,7 @@ macro_rules! free_var_references { // TODO: replace with just a `serde_json::Value` // https://linear.app/vercel/issue/WEB-1641/compiletimedefinevalue-should-just-use-serde-jsonvalue #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(Debug, Clone, Hash, PartialOrd, Ord)] +#[derive(Debug, Clone, Hash)] pub enum CompileTimeDefineValue { Bool(bool), String(RcStr), diff --git a/crates/turbopack-core/src/environment.rs b/crates/turbopack-core/src/environment.rs index f75350592bd1e..89f40160dab6f 100644 --- a/crates/turbopack-core/src/environment.rs +++ b/crates/turbopack-core/src/environment.rs @@ -53,7 +53,7 @@ impl Environment { } #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(PartialOrd, Ord, Debug, Hash, Clone, Copy)] +#[derive(Debug, Hash, Clone, Copy)] pub enum ExecutionEnvironment { NodeJsBuildTime(Vc), NodeJsLambda(Vc), diff --git a/crates/turbopack-core/src/ident.rs b/crates/turbopack-core/src/ident.rs index e63d4eb6c1c3c..a41515fe25eac 100644 --- a/crates/turbopack-core/src/ident.rs +++ b/crates/turbopack-core/src/ident.rs @@ -8,7 +8,7 @@ use turbo_tasks_hash::{encode_hex, hash_xxh3_hash64, DeterministicHash, Xxh3Hash use crate::resolve::ModulePart; #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(Clone, Debug, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, Hash)] pub struct AssetIdent { /// The primary path of the asset pub path: Vc, diff --git a/crates/turbopack-core/src/reference_type.rs b/crates/turbopack-core/src/reference_type.rs index 7db3416319b5c..13a795b6d02ac 100644 --- a/crates/turbopack-core/src/reference_type.rs +++ b/crates/turbopack-core/src/reference_type.rs @@ -27,20 +27,20 @@ impl InnerAssets { // behavior. #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(Debug, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Hash)] pub enum CommonJsReferenceSubType { Custom(u8), Undefined, } #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(Debug, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Hash)] pub enum ImportWithType { Json, } #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(Debug, Default, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, Default, Clone, Hash)] pub enum EcmaScriptModulesReferenceSubType { ImportPart(Vc), Import, @@ -163,7 +163,7 @@ impl ImportContext { } #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(Debug, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Hash)] pub enum CssReferenceSubType { AtImport(Option>), Compose, @@ -178,7 +178,7 @@ pub enum CssReferenceSubType { } #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(Debug, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Hash)] pub enum UrlReferenceSubType { EcmaScriptNewUrl, CssUrl, @@ -187,7 +187,7 @@ pub enum UrlReferenceSubType { } #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(Debug, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Hash)] pub enum TypeScriptReferenceSubType { Custom(u8), Undefined, @@ -196,7 +196,7 @@ pub enum TypeScriptReferenceSubType { // TODO(sokra) this was next.js specific values. We want to solve this in a // different way. #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(Debug, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Hash)] pub enum EntryReferenceSubType { Web, Page, @@ -212,7 +212,7 @@ pub enum EntryReferenceSubType { } #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(Debug, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Hash)] pub enum ReferenceType { CommonJs(CommonJsReferenceSubType), EcmaScriptModules(EcmaScriptModulesReferenceSubType), diff --git a/crates/turbopack-core/src/resolve/mod.rs b/crates/turbopack-core/src/resolve/mod.rs index aa92072afed30..1e7061684d113 100644 --- a/crates/turbopack-core/src/resolve/mod.rs +++ b/crates/turbopack-core/src/resolve/mod.rs @@ -392,7 +392,9 @@ impl ModuleResolveResultOption { } } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, TraceRawVcs, TaskInput)] +#[derive( + Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, TraceRawVcs, TaskInput, +)] pub enum ExternalType { Url, CommonJs, @@ -425,7 +427,7 @@ pub enum ResolveResultItem { /// resolving. A primary factor is the actual request string, but there are /// other factors like exports conditions that can affect resolting and become /// part of the key (assuming the condition is unknown at compile time) -#[derive(Clone, Debug, Default, Hash, Ord, PartialOrd)] +#[derive(Clone, Debug, Default, Hash)] #[turbo_tasks::value(serialization = "auto_for_input")] pub struct RequestKey { pub request: Option, diff --git a/crates/turbopack-core/src/resolve/pattern.rs b/crates/turbopack-core/src/resolve/pattern.rs index 825a3c766ff2d..e2d84edb1908c 100644 --- a/crates/turbopack-core/src/resolve/pattern.rs +++ b/crates/turbopack-core/src/resolve/pattern.rs @@ -11,7 +11,7 @@ use turbo_tasks_fs::{ }; #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(PartialOrd, Ord, Hash, Clone, Debug, Default)] +#[derive(Hash, Clone, Debug, Default)] pub enum Pattern { Constant(RcStr), #[default] @@ -764,12 +764,26 @@ impl ValueToString for Pattern { } } -#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, TraceRawVcs, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Clone, TraceRawVcs, Serialize, Deserialize)] pub enum PatternMatch { File(RcStr, Vc), Directory(RcStr, Vc), } +impl PatternMatch { + pub fn path(&self) -> Vc { + match *self { + PatternMatch::File(_, path) | PatternMatch::Directory(_, path) => path, + } + } + + pub fn name(&self) -> &str { + match self { + PatternMatch::File(name, _) | PatternMatch::Directory(name, _) => name.as_str(), + } + } +} + // TODO this isn't super efficient // avoid storing a large list of matches #[turbo_tasks::value(transparent)] @@ -1087,7 +1101,7 @@ pub async fn read_matches( for (pos, nested) in nested.into_iter() { results.extend(nested.await?.iter().cloned().map(|p| (pos, p))); } - results.sort(); + results.sort_by(|(a, am), (b, bm)| (*a).cmp(b).then_with(|| am.name().cmp(bm.name()))); Ok(Vc::cell( results.into_iter().map(|(_, p)| p).collect::>(), )) diff --git a/crates/turbopack-core/src/source_pos.rs b/crates/turbopack-core/src/source_pos.rs index 98f19f0e83bf6..d4686ba29782f 100644 --- a/crates/turbopack-core/src/source_pos.rs +++ b/crates/turbopack-core/src/source_pos.rs @@ -14,6 +14,7 @@ const U8_CR: u8 = 0x0D; Eq, Copy, Clone, + Hash, PartialOrd, Ord, TaskInput, diff --git a/crates/turbopack-core/src/target.rs b/crates/turbopack-core/src/target.rs index 2e51b5fb4a57d..e07862b515e78 100644 --- a/crates/turbopack-core/src/target.rs +++ b/crates/turbopack-core/src/target.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use turbo_tasks::{trace::TraceRawVcs, Vc}; #[turbo_tasks::value(shared, serialization = "auto_for_input")] -#[derive(PartialOrd, Ord, Hash, Debug, Copy, Clone)] +#[derive(Hash, Debug, Copy, Clone)] pub struct CompileTarget { /// pub arch: Arch, @@ -157,9 +157,7 @@ impl CompileTarget { } } -#[derive( - PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy, Clone, TraceRawVcs, Serialize, Deserialize, -)] +#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone, TraceRawVcs, Serialize, Deserialize)] #[repr(u8)] #[non_exhaustive] pub enum Arch { @@ -200,9 +198,7 @@ impl Display for Arch { } } -#[derive( - PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy, Clone, TraceRawVcs, Serialize, Deserialize, -)] +#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone, TraceRawVcs, Serialize, Deserialize)] #[repr(u8)] #[non_exhaustive] pub enum Platform { @@ -239,9 +235,7 @@ impl Display for Platform { } } -#[derive( - PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy, Clone, TraceRawVcs, Serialize, Deserialize, -)] +#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone, TraceRawVcs, Serialize, Deserialize)] #[repr(u8)] pub enum Endianness { Big, @@ -263,9 +257,7 @@ impl Display for Endianness { } } -#[derive( - PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy, Clone, TraceRawVcs, Serialize, Deserialize, -)] +#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone, TraceRawVcs, Serialize, Deserialize)] #[repr(u8)] pub enum Libc { Glibc, diff --git a/crates/turbopack-dev-server/src/source/headers.rs b/crates/turbopack-dev-server/src/source/headers.rs index e0adcedce601d..55699bf070fcf 100644 --- a/crates/turbopack-dev-server/src/source/headers.rs +++ b/crates/turbopack-dev-server/src/source/headers.rs @@ -10,9 +10,7 @@ pub struct Headers(BTreeMap); /// The value of an http header. HTTP headers might contain non-utf-8 bytes. An /// header might also occur multiple times. -#[derive( - Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, TraceRawVcs, Serialize, Deserialize, -)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, TraceRawVcs, Serialize, Deserialize)] #[serde(untagged)] pub enum HeaderValue { SingleString(String), @@ -21,21 +19,6 @@ pub enum HeaderValue { MultiBytes(Vec>), } -impl PartialOrd for Headers { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Headers { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.0 - .len() - .cmp(&other.0.len()) - .then_with(|| self.0.iter().cmp(other.0.iter())) - } -} - impl std::ops::Deref for Headers { type Target = BTreeMap; fn deref(&self) -> &Self::Target { diff --git a/crates/turbopack-dev-server/src/source/mod.rs b/crates/turbopack-dev-server/src/source/mod.rs index 26870a1f399c3..0518db659f071 100644 --- a/crates/turbopack-dev-server/src/source/mod.rs +++ b/crates/turbopack-dev-server/src/source/mod.rs @@ -177,7 +177,7 @@ impl HeaderList { /// `ContentSource::vary()`. So make sure to request all information that's /// needed. #[turbo_tasks::value(shared, serialization = "auto_for_input")] -#[derive(Clone, Debug, PartialOrd, Ord, Hash, Default)] +#[derive(Clone, Debug, Hash, Default)] pub struct ContentSourceData { /// HTTP method, if requested. pub method: Option, @@ -247,9 +247,7 @@ impl ValueDefault for Body { } /// Filter function that describes which information is required. -#[derive( - Debug, Clone, PartialEq, Eq, TraceRawVcs, Hash, PartialOrd, Ord, Serialize, Deserialize, -)] +#[derive(Debug, Clone, PartialEq, Eq, TraceRawVcs, Hash, Serialize, Deserialize)] pub enum ContentSourceDataFilter { All, Subset(BTreeSet), @@ -314,7 +312,7 @@ impl ContentSourceDataFilter { /// ContentSource. By sending these information ContentSource responses are /// cached-keyed by them and they can access them. #[turbo_tasks::value(shared, serialization = "auto_for_input")] -#[derive(Debug, Default, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, Default, Clone, Hash)] pub struct ContentSourceDataVary { pub method: bool, pub url: bool, diff --git a/crates/turbopack-dev-server/src/source/query.rs b/crates/turbopack-dev-server/src/source/query.rs index 61bcd2f1b25cc..372985ae7619b 100644 --- a/crates/turbopack-dev-server/src/source/query.rs +++ b/crates/turbopack-dev-server/src/source/query.rs @@ -21,21 +21,6 @@ impl Query { } } -impl PartialOrd for Query { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Query { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.0 - .len() - .cmp(&other.0.len()) - .then_with(|| self.0.iter().cmp(other.0.iter())) - } -} - impl std::ops::Deref for Query { type Target = BTreeMap; fn deref(&self) -> &Self::Target { @@ -49,9 +34,7 @@ impl DerefMut for Query { } } -#[derive( - Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, TraceRawVcs, Serialize, Deserialize, -)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, TraceRawVcs, Serialize, Deserialize)] #[serde(untagged)] pub enum QueryValue { /// Simple string value, might be an empty string when there is no value diff --git a/crates/turbopack-dev-server/src/source/route_tree.rs b/crates/turbopack-dev-server/src/source/route_tree.rs index 58f2c188a1f4d..2e29ee5df2117 100644 --- a/crates/turbopack-dev-server/src/source/route_tree.rs +++ b/crates/turbopack-dev-server/src/source/route_tree.rs @@ -9,7 +9,7 @@ use super::{GetContentSourceContent, GetContentSourceContents}; /// The type of the route. THis will decide about the remaining segements of the /// route after the base. -#[derive(TaskInput, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs)] +#[derive(TaskInput, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, TraceRawVcs)] pub enum RouteType { Exact, CatchAll, @@ -18,7 +18,7 @@ pub enum RouteType { } /// Some normal segment of a route. -#[derive(TaskInput, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs)] +#[derive(TaskInput, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, TraceRawVcs)] pub enum BaseSegment { Static(RcStr), Dynamic, diff --git a/crates/turbopack-ecmascript-hmr-protocol/src/lib.rs b/crates/turbopack-ecmascript-hmr-protocol/src/lib.rs index 721cee9b7dfe3..51eba9ddbf535 100644 --- a/crates/turbopack-ecmascript-hmr-protocol/src/lib.rs +++ b/crates/turbopack-ecmascript-hmr-protocol/src/lib.rs @@ -8,7 +8,7 @@ use turbopack_core::{ source_pos::SourcePos, }; -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct ResourceIdentifier { pub path: String, pub headers: Option>, diff --git a/crates/turbopack-ecmascript-runtime/src/runtime_type.rs b/crates/turbopack-ecmascript-runtime/src/runtime_type.rs index 41978bc0051b5..b44bcb69aa4c7 100644 --- a/crates/turbopack-ecmascript-runtime/src/runtime_type.rs +++ b/crates/turbopack-ecmascript-runtime/src/runtime_type.rs @@ -1,9 +1,7 @@ use serde::{Deserialize, Serialize}; use turbo_tasks::trace::TraceRawVcs; -#[derive( - Serialize, Deserialize, Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, TraceRawVcs, -)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, Hash, PartialEq, Eq, TraceRawVcs)] pub enum RuntimeType { Development, Production, diff --git a/crates/turbopack-ecmascript/src/analyzer/imports.rs b/crates/turbopack-ecmascript/src/analyzer/imports.rs index ba874d0c3ca44..e9d797cc62349 100644 --- a/crates/turbopack-ecmascript/src/analyzer/imports.rs +++ b/crates/turbopack-ecmascript/src/analyzer/imports.rs @@ -17,7 +17,7 @@ use super::{top_level_await::has_top_level_await, JsValue, ModuleValue}; use crate::tree_shake::{find_turbopack_part_id_in_asserts, PartId}; #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(Default, Debug, Clone, Hash, PartialOrd, Ord)] +#[derive(Default, Debug, Clone, Hash)] pub struct ImportAnnotations { // TODO store this in more structured way #[turbo_tasks(trace_ignore)] @@ -135,7 +135,7 @@ pub(crate) struct ImportMap { has_top_level_await: bool, } -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(crate) enum ImportedSymbol { ModuleEvaluation, Symbol(JsWord), @@ -143,7 +143,7 @@ pub(crate) enum ImportedSymbol { Part(u32), } -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(crate) struct ImportMapReference { pub module_path: JsWord, pub imported_symbol: ImportedSymbol, diff --git a/crates/turbopack-ecmascript/src/lib.rs b/crates/turbopack-ecmascript/src/lib.rs index aeb15d0b583a2..1f296bca859cc 100644 --- a/crates/turbopack-ecmascript/src/lib.rs +++ b/crates/turbopack-ecmascript/src/lib.rs @@ -91,7 +91,7 @@ use crate::{ }; #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(PartialOrd, Ord, Hash, Debug, Clone, Copy, Default, TaskInput)] +#[derive(Hash, Debug, Clone, Copy, Default, TaskInput)] pub enum SpecifiedModuleType { #[default] Automatic, @@ -124,7 +124,7 @@ pub enum TreeShakingMode { pub struct OptionTreeShaking(pub Option); #[turbo_tasks::value(shared, serialization = "auto_for_input")] -#[derive(PartialOrd, Ord, Hash, Debug, Default, Copy, Clone)] +#[derive(Hash, Debug, Default, Copy, Clone)] pub struct EcmascriptOptions { pub refresh: bool, /// variant of tree shaking to use @@ -150,7 +150,7 @@ pub struct EcmascriptOptions { } #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(PartialOrd, Ord, Hash, Debug, Copy, Clone)] +#[derive(Hash, Debug, Copy, Clone)] pub enum EcmascriptModuleAssetType { /// Module with EcmaScript code Ecmascript, diff --git a/crates/turbopack-ecmascript/src/references/constant_condition.rs b/crates/turbopack-ecmascript/src/references/constant_condition.rs index 17e51238d5494..8f5f64e5b204d 100644 --- a/crates/turbopack-ecmascript/src/references/constant_condition.rs +++ b/crates/turbopack-ecmascript/src/references/constant_condition.rs @@ -10,7 +10,7 @@ use crate::{ }; #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(Debug, Clone, Copy, Hash, PartialOrd, Ord)] +#[derive(Debug, Clone, Copy, Hash)] pub enum ConstantConditionValue { Truthy, Falsy, diff --git a/crates/turbopack-ecmascript/src/references/esm/url.rs b/crates/turbopack-ecmascript/src/references/esm/url.rs index 6a2bfe89c9589..89bf7cf20dc74 100644 --- a/crates/turbopack-ecmascript/src/references/esm/url.rs +++ b/crates/turbopack-ecmascript/src/references/esm/url.rs @@ -31,7 +31,7 @@ use crate::{ /// This allows to construct url depends on the different building context, /// e.g. SSR, CSR, or Node.js. #[turbo_tasks::value(shared)] -#[derive(Debug, Copy, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, Copy, Clone, Hash)] pub enum UrlRewriteBehavior { /// Omits base, resulting in a relative URL. Relative, diff --git a/crates/turbopack-ecmascript/src/references/external_module.rs b/crates/turbopack-ecmascript/src/references/external_module.rs index 209145d5aa7c2..5108f6b37bf27 100644 --- a/crates/turbopack-ecmascript/src/references/external_module.rs +++ b/crates/turbopack-ecmascript/src/references/external_module.rs @@ -27,7 +27,9 @@ fn layer() -> Vc { Vc::cell("external".into()) } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, TraceRawVcs, TaskInput)] +#[derive( + Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, TraceRawVcs, TaskInput, Hash, +)] pub enum CachedExternalType { CommonJs, EcmaScriptViaRequire, diff --git a/crates/turbopack-ecmascript/src/references/pattern_mapping.rs b/crates/turbopack-ecmascript/src/references/pattern_mapping.rs index 66fed95a03732..c0c87b707f51d 100644 --- a/crates/turbopack-ecmascript/src/references/pattern_mapping.rs +++ b/crates/turbopack-ecmascript/src/references/pattern_mapping.rs @@ -73,7 +73,7 @@ pub(crate) enum PatternMapping { Map(IndexMap), } -#[derive(PartialOrd, Ord, Hash, Debug, Copy, Clone)] +#[derive(Hash, Debug, Copy, Clone)] #[turbo_tasks::value(serialization = "auto_for_input")] pub(crate) enum ResolveType { AsyncChunkLoader, diff --git a/crates/turbopack-ecmascript/src/transform/mod.rs b/crates/turbopack-ecmascript/src/transform/mod.rs index 121ee1bfc4bfe..6bc59e13b12e4 100644 --- a/crates/turbopack-ecmascript/src/transform/mod.rs +++ b/crates/turbopack-ecmascript/src/transform/mod.rs @@ -26,7 +26,7 @@ use turbopack_core::{ }; #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(Debug, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Hash)] pub enum EcmascriptInputTransform { CommonJs, Plugin(Vc), @@ -100,7 +100,7 @@ impl CustomTransformer for TransformPlugin { } #[turbo_tasks::value(transparent, serialization = "auto_for_input")] -#[derive(Debug, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Hash)] pub struct EcmascriptInputTransforms(Vec); #[turbo_tasks::value_impl] diff --git a/crates/turbopack-mdx/src/lib.rs b/crates/turbopack-mdx/src/lib.rs index bfc86eb6b6735..a02b29837926c 100644 --- a/crates/turbopack-mdx/src/lib.rs +++ b/crates/turbopack-mdx/src/lib.rs @@ -32,7 +32,7 @@ fn modifier() -> Vc { } #[turbo_tasks::value(shared)] -#[derive(PartialOrd, Ord, Hash, Debug, Clone)] +#[derive(Hash, Debug, Clone)] #[serde(rename_all = "camelCase")] pub enum MdxParseConstructs { Commonmark, @@ -43,7 +43,7 @@ pub enum MdxParseConstructs { /// into mdxjs. This is thin, near straightforward subset of mdxjs::Options to /// enable turbo tasks. #[turbo_tasks::value(shared)] -#[derive(PartialOrd, Ord, Hash, Debug, Clone)] +#[derive(Hash, Debug, Clone)] #[serde(rename_all = "camelCase", default)] pub struct MdxTransformOptions { pub development: Option, diff --git a/crates/turbopack-node/src/evaluate.rs b/crates/turbopack-node/src/evaluate.rs index 49fc68c41ce56..505755b12c711 100644 --- a/crates/turbopack-node/src/evaluate.rs +++ b/crates/turbopack-node/src/evaluate.rs @@ -456,7 +456,7 @@ async fn basic_compute( compute(evaluate_context, sender).await } -#[derive(Clone, PartialEq, Eq, TaskInput)] +#[derive(Clone, PartialEq, Eq, Hash, TaskInput, Debug, Serialize, Deserialize)] struct BasicEvaluateContext { module_asset: Vc>, cwd: Vc, diff --git a/crates/turbopack-node/src/render/render_proxy.rs b/crates/turbopack-node/src/render/render_proxy.rs index 0d76394aa8b1a..7e69575aff122 100644 --- a/crates/turbopack-node/src/render/render_proxy.rs +++ b/crates/turbopack-node/src/render/render_proxy.rs @@ -5,8 +5,10 @@ use futures::{ pin_mut, SinkExt, StreamExt, TryStreamExt, }; use parking_lot::Mutex; +use serde::{Deserialize, Serialize}; use turbo_tasks::{ - duration_span, mark_finished, prevent_gc, util::SharedError, RawVc, RcStr, ValueToString, Vc, + duration_span, mark_finished, prevent_gc, util::SharedError, RawVc, RcStr, TaskInput, + ValueToString, Vc, }; use turbo_tasks_bytes::{Bytes, Stream}; use turbo_tasks_env::ProcessEnv; @@ -44,7 +46,7 @@ pub async fn render_proxy( body: Vc, debug: bool, ) -> Result> { - let render = render_stream( + let render = render_stream(RenderStreamOptions { cwd, env, path, @@ -57,7 +59,7 @@ pub async fn render_proxy( data, body, debug, - ) + }) .await?; let mut stream = render.read(); @@ -146,8 +148,8 @@ struct RenderStreamSender { #[turbo_tasks::value(transparent)] struct RenderStream(#[turbo_tasks(trace_ignore)] Stream); -#[turbo_tasks::function] -fn render_stream( +#[derive(Clone, Debug, TaskInput, PartialEq, Eq, Hash, Serialize, Deserialize)] +struct RenderStreamOptions { cwd: Vc, env: Vc>, path: Vc, @@ -160,7 +162,10 @@ fn render_stream( data: Vc, body: Vc, debug: bool, -) -> Vc { +} + +#[turbo_tasks::function] +fn render_stream(options: RenderStreamOptions) -> Vc { // TODO: The way we invoke render_stream_internal as side effect is not // GC-safe, so we disable GC for this task. prevent_gc(); @@ -180,17 +185,7 @@ fn render_stream( // run the evaluation as side effect let _ = render_stream_internal( - cwd, - env, - path, - module, - runtime_entries, - chunking_context, - intermediate_output_path, - output_root, - project_dir, - data, - body, + options, RenderStreamSender { get: Box::new(move || { if let Some(sender) = initial.lock().take() { @@ -205,7 +200,6 @@ fn render_stream( }), } .cell(), - debug, ); let raw: RawVc = cell.into(); @@ -214,20 +208,24 @@ fn render_stream( #[turbo_tasks::function] async fn render_stream_internal( - cwd: Vc, - env: Vc>, - path: Vc, - module: Vc>, - runtime_entries: Vc, - chunking_context: Vc>, - intermediate_output_path: Vc, - output_root: Vc, - project_dir: Vc, - data: Vc, - body: Vc, + options: RenderStreamOptions, sender: Vc, - debug: bool, ) -> Result> { + let RenderStreamOptions { + cwd, + env, + path, + module, + runtime_entries, + chunking_context, + intermediate_output_path, + output_root, + project_dir, + data, + body, + debug, + } = options; + mark_finished(); let Ok(sender) = sender.await else { // Impossible to handle the error in a good way. diff --git a/crates/turbopack-node/src/render/render_static.rs b/crates/turbopack-node/src/render/render_static.rs index bcc65bc688cf0..05e3a161f6379 100644 --- a/crates/turbopack-node/src/render/render_static.rs +++ b/crates/turbopack-node/src/render/render_static.rs @@ -5,8 +5,10 @@ use futures::{ pin_mut, SinkExt, StreamExt, TryStreamExt, }; use parking_lot::Mutex; +use serde::{Deserialize, Serialize}; use turbo_tasks::{ - duration_span, mark_finished, prevent_gc, util::SharedError, RawVc, ValueToString, Vc, + duration_span, mark_finished, prevent_gc, util::SharedError, RawVc, TaskInput, ValueToString, + Vc, }; use turbo_tasks_bytes::{Bytes, Stream}; use turbo_tasks_env::ProcessEnv; @@ -85,7 +87,7 @@ pub async fn render_static( data: Vc, debug: bool, ) -> Result> { - let render = render_stream( + let render = render_stream(RenderStreamOptions { cwd, env, path, @@ -98,7 +100,7 @@ pub async fn render_static( project_dir, data, debug, - ) + }) .await?; let mut stream = render.read(); @@ -197,8 +199,8 @@ struct RenderStreamSender { #[turbo_tasks::value(transparent)] struct RenderStream(#[turbo_tasks(trace_ignore)] Stream); -#[turbo_tasks::function] -fn render_stream( +#[derive(Clone, Debug, TaskInput, PartialEq, Eq, Hash, Deserialize, Serialize)] +struct RenderStreamOptions { cwd: Vc, env: Vc>, path: Vc, @@ -211,7 +213,10 @@ fn render_stream( project_dir: Vc, data: Vc, debug: bool, -) -> Vc { +} + +#[turbo_tasks::function] +fn render_stream(options: RenderStreamOptions) -> Vc { // TODO: The way we invoke render_stream_internal as side effect is not // GC-safe, so we disable GC for this task. prevent_gc(); @@ -231,17 +236,7 @@ fn render_stream( // run the evaluation as side effect let _ = render_stream_internal( - cwd, - env, - path, - module, - runtime_entries, - fallback_page, - chunking_context, - intermediate_output_path, - output_root, - project_dir, - data, + options, RenderStreamSender { get: Box::new(move || { if let Some(sender) = initial.lock().take() { @@ -256,7 +251,6 @@ fn render_stream( }), } .cell(), - debug, ); let raw: RawVc = cell.into(); @@ -265,20 +259,24 @@ fn render_stream( #[turbo_tasks::function] async fn render_stream_internal( - cwd: Vc, - env: Vc>, - path: Vc, - module: Vc>, - runtime_entries: Vc, - fallback_page: Vc, - chunking_context: Vc>, - intermediate_output_path: Vc, - output_root: Vc, - project_dir: Vc, - data: Vc, + options: RenderStreamOptions, sender: Vc, - debug: bool, ) -> Result> { + let RenderStreamOptions { + cwd, + env, + path, + module, + runtime_entries, + fallback_page, + chunking_context, + intermediate_output_path, + output_root, + project_dir, + data, + debug, + } = options; + mark_finished(); let Ok(sender) = sender.await else { // Impossible to handle the error in a good way. diff --git a/crates/turbopack-node/src/transforms/postcss.rs b/crates/turbopack-node/src/transforms/postcss.rs index 1b1e1dc2332e4..76497668ffdcc 100644 --- a/crates/turbopack-node/src/transforms/postcss.rs +++ b/crates/turbopack-node/src/transforms/postcss.rs @@ -45,7 +45,7 @@ struct PostCssProcessingResult { } #[derive( - Default, Copy, Clone, PartialEq, Eq, Debug, TraceRawVcs, Serialize, Deserialize, TaskInput, + Default, Copy, Clone, PartialEq, Eq, Hash, Debug, TraceRawVcs, Serialize, Deserialize, TaskInput, )] pub enum PostCssConfigLocation { #[default] diff --git a/crates/turbopack-node/src/transforms/webpack.rs b/crates/turbopack-node/src/transforms/webpack.rs index e34988866ef09..a218d3f21a699 100644 --- a/crates/turbopack-node/src/transforms/webpack.rs +++ b/crates/turbopack-node/src/transforms/webpack.rs @@ -325,7 +325,7 @@ pub enum InfoMessage { Log(LogInfo), } -#[derive(Deserialize, Debug, Clone, TaskInput)] +#[derive(Debug, Clone, TaskInput, Hash, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct WebpackResolveOptions { @@ -357,7 +357,7 @@ pub enum ResponseMessage { Resolve { path: RcStr }, } -#[derive(Clone, PartialEq, Eq, TaskInput)] +#[derive(Clone, PartialEq, Eq, Hash, TaskInput, Serialize, Deserialize, Debug)] pub struct WebpackLoaderContext { pub module_asset: Vc>, pub cwd: Vc, diff --git a/crates/turbopack-nodejs/src/chunking_context.rs b/crates/turbopack-nodejs/src/chunking_context.rs index f55b6e6e55250..169623d9955ec 100644 --- a/crates/turbopack-nodejs/src/chunking_context.rs +++ b/crates/turbopack-nodejs/src/chunking_context.rs @@ -61,7 +61,7 @@ impl NodeJsChunkingContextBuilder { /// A chunking context for build mode. #[turbo_tasks::value(serialization = "auto_for_input")] -#[derive(Debug, Clone, Hash, PartialOrd, Ord)] +#[derive(Debug, Clone, Hash)] pub struct NodeJsChunkingContext { /// This path get stripped off of chunk paths before generating output asset /// paths. diff --git a/crates/turbopack/src/module_options/module_rule.rs b/crates/turbopack/src/module_options/module_rule.rs index 6996a4350606a..710be28eaacad 100644 --- a/crates/turbopack/src/module_options/module_rule.rs +++ b/crates/turbopack/src/module_options/module_rule.rs @@ -96,7 +96,7 @@ pub enum ModuleRuleEffect { } #[turbo_tasks::value(serialization = "auto_for_input", shared)] -#[derive(PartialOrd, Ord, Hash, Debug, Copy, Clone)] +#[derive(Hash, Debug, Copy, Clone)] pub enum ModuleType { Ecmascript { transforms: Vc,