diff --git a/crates/turbo-tasks-macros/src/func.rs b/crates/turbo-tasks-macros/src/func.rs index a02eb175ae06b..3e1a414cfa065 100644 --- a/crates/turbo-tasks-macros/src/func.rs +++ b/crates/turbo-tasks-macros/src/func.rs @@ -14,6 +14,7 @@ pub struct TurboFn { // block: Block, ident: Ident, output: Type, + this: Option, inputs: Vec, } @@ -56,6 +57,7 @@ impl TurboFn { } let mut raw_inputs = original_signature.inputs.iter(); + let mut this = None; let mut inputs = Vec::with_capacity(raw_inputs.len()); if let Some(possibly_receiver) = raw_inputs.next() { @@ -142,7 +144,7 @@ impl TurboFn { _ => {} } - inputs.push(Input { + this = Some(Input { ident: Ident::new("self", self_token.span()), ty: parse_quote! { turbo_tasks::Vc }, }); @@ -160,7 +162,7 @@ impl TurboFn { return None; } - let ident = if let Pat::Ident(ident) = &*typed.pat { + if let Pat::Ident(ident) = &*typed.pat { if ident.ident == "self" { if let DefinitionContext::NakedFn { .. } = definition_context { // The function is not associated. The compiler will emit an error @@ -174,6 +176,13 @@ impl TurboFn { // if the user provided an invalid receiver type // when // calling `into_concrete`. + + let ident = ident.ident.clone(); + + this = Some(Input { + ident, + ty: parse_quote! { turbo_tasks::Vc }, + }); } else { match definition_context { DefinitionContext::NakedFn { .. } @@ -192,18 +201,22 @@ impl TurboFn { return None; } } - } + let ident = ident.ident.clone(); - ident.ident.clone() + inputs.push(Input { + ident, + ty: (*typed.ty).clone(), + }); + } } else { // We can't support destructuring patterns (or other kinds of patterns). - Ident::new("arg1", typed.pat.span()) - }; + let ident = Ident::new("arg1", typed.pat.span()); - inputs.push(Input { - ident, - ty: (*typed.ty).clone(), - }); + inputs.push(Input { + ident, + ty: (*typed.ty).clone(), + }); + } } } } @@ -234,6 +247,7 @@ impl TurboFn { Some(TurboFn { ident: original_signature.ident.clone(), output, + this, inputs, }) } @@ -242,8 +256,10 @@ impl TurboFn { /// converted to a standard turbo_tasks function signature. pub fn signature(&self) -> Signature { let exposed_inputs: Punctuated<_, Token![,]> = self - .inputs - .iter() + .this + .as_ref() + .into_iter() + .chain(self.inputs.iter()) .map(|input| { FnArg::Typed(PatType { attrs: Default::default(), @@ -288,21 +304,38 @@ impl TurboFn { .collect() } + fn converted_this(&self) -> Option { + self.this.as_ref().map(|Input { ty: _, ident }| { + parse_quote! { + turbo_tasks::Vc::into_raw(#ident) + } + }) + } + /// The block of the exposed function for a dynamic dispatch call to the /// given trait. pub fn dynamic_block(&self, trait_type_id_ident: &Ident) -> Block { let ident = &self.ident; let output = &self.output; - let converted_inputs = self.converted_inputs(); - parse_quote! { - { - <#output as turbo_tasks::task::TaskOutput>::try_from_raw_vc( - turbo_tasks::trait_call( - *#trait_type_id_ident, - std::borrow::Cow::Borrowed(stringify!(#ident)), - vec![#converted_inputs], + if let Some(converted_this) = self.converted_this() { + let converted_inputs = self.converted_inputs(); + parse_quote! { + { + <#output as turbo_tasks::task::TaskOutput>::try_from_raw_vc( + turbo_tasks::trait_call( + *#trait_type_id_ident, + std::borrow::Cow::Borrowed(stringify!(#ident)), + #converted_this, + vec![#converted_inputs], + ) ) - ) + } + } + } else { + parse_quote! { + { + unimplemented!("trait methods without self are not yet supported") + } } } } @@ -312,17 +345,35 @@ impl TurboFn { pub fn static_block(&self, native_function_id_ident: &Ident) -> Block { let output = &self.output; let converted_inputs = self.converted_inputs(); - parse_quote! { - { - <#output as turbo_tasks::task::TaskOutput>::try_from_raw_vc( - turbo_tasks::dynamic_call( - *#native_function_id_ident, - vec![#converted_inputs], + if let Some(converted_this) = self.converted_this() { + parse_quote! { + { + <#output as turbo_tasks::task::TaskOutput>::try_from_raw_vc( + turbo_tasks::dynamic_this_call( + *#native_function_id_ident, + #converted_this, + vec![#converted_inputs], + ) ) - ) + } + } + } else { + parse_quote! { + { + <#output as turbo_tasks::task::TaskOutput>::try_from_raw_vc( + turbo_tasks::dynamic_call( + *#native_function_id_ident, + vec![#converted_inputs], + ) + ) + } } } } + + pub(crate) fn is_method(&self) -> bool { + self.this.is_some() + } } fn return_type_to_type(return_type: &ReturnType) -> Type { @@ -454,13 +505,15 @@ impl DefinitionContext { pub struct NativeFn { function_path_string: String, function_path: ExprPath, + is_method: bool, } impl NativeFn { - pub fn new(function_path_string: &str, function_path: &ExprPath) -> NativeFn { + pub fn new(function_path_string: &str, function_path: &ExprPath, is_method: bool) -> NativeFn { NativeFn { function_path_string: function_path_string.to_owned(), function_path: function_path.clone(), + is_method, } } @@ -472,13 +525,23 @@ impl NativeFn { let Self { function_path_string, function_path, + is_method, } = self; - parse_quote! { - turbo_tasks::macro_helpers::Lazy::new(|| { - #[allow(deprecated)] - turbo_tasks::NativeFunction::new(#function_path_string.to_owned(), #function_path) - }) + if *is_method { + parse_quote! { + turbo_tasks::macro_helpers::Lazy::new(|| { + #[allow(deprecated)] + turbo_tasks::NativeFunction::new_method(#function_path_string.to_owned(), #function_path) + }) + } + } else { + parse_quote! { + turbo_tasks::macro_helpers::Lazy::new(|| { + #[allow(deprecated)] + turbo_tasks::NativeFunction::new_function(#function_path_string.to_owned(), #function_path) + }) + } } } diff --git a/crates/turbo-tasks-macros/src/function_macro.rs b/crates/turbo-tasks-macros/src/function_macro.rs index 4d805de9ad711..4c047d0e067a5 100644 --- a/crates/turbo-tasks-macros/src/function_macro.rs +++ b/crates/turbo-tasks-macros/src/function_macro.rs @@ -30,7 +30,11 @@ pub fn function(_args: TokenStream, input: TokenStream) -> TokenStream { let mut inline_signature = sig.clone(); inline_signature.ident = inline_function_ident; - let native_fn = NativeFn::new(&ident.to_string(), &inline_function_path); + let native_fn = NativeFn::new( + &ident.to_string(), + &inline_function_path, + turbo_fn.is_method(), + ); let native_function_ident = get_native_function_ident(ident); let native_function_ty = native_fn.ty(); let native_function_def = native_fn.definition(); diff --git a/crates/turbo-tasks-macros/src/lib.rs b/crates/turbo-tasks-macros/src/lib.rs index b7a0e3bcca25c..092eeb151b552 100644 --- a/crates/turbo-tasks-macros/src/lib.rs +++ b/crates/turbo-tasks-macros/src/lib.rs @@ -166,6 +166,13 @@ pub fn function(args: TokenStream, input: TokenStream) -> TokenStream { function_macro::function(args, input) } +#[allow_internal_unstable(min_specialization, into_future, trivial_bounds)] +#[proc_macro_error] +#[proc_macro_attribute] +pub fn test_tt(_args: TokenStream, input: TokenStream) -> TokenStream { + derive::derive_value_debug(input) +} + #[allow_internal_unstable(min_specialization, into_future, trivial_bounds)] #[proc_macro_error] #[proc_macro_attribute] diff --git a/crates/turbo-tasks-macros/src/value_impl_macro.rs b/crates/turbo-tasks-macros/src/value_impl_macro.rs index fc8773b79226a..235268169d125 100644 --- a/crates/turbo-tasks-macros/src/value_impl_macro.rs +++ b/crates/turbo-tasks-macros/src/value_impl_macro.rs @@ -120,6 +120,7 @@ pub fn value_impl(args: TokenStream, input: TokenStream) -> TokenStream { let native_fn = NativeFn::new( &format!("{ty}::{ident}", ty = ty.to_token_stream()), &inline_function_path, + turbo_fn.is_method(), ); let native_function_ident = get_inherent_impl_function_ident(ty_ident, ident); @@ -225,6 +226,7 @@ pub fn value_impl(args: TokenStream, input: TokenStream) -> TokenStream { trait_path = trait_path.to_token_stream() ), &inline_function_path, + turbo_fn.is_method(), ); let native_function_ident = diff --git a/crates/turbo-tasks-macros/src/value_trait_macro.rs b/crates/turbo-tasks-macros/src/value_trait_macro.rs index 23ee201369b1f..931c26e69944d 100644 --- a/crates/turbo-tasks-macros/src/value_trait_macro.rs +++ b/crates/turbo-tasks-macros/src/value_trait_macro.rs @@ -108,8 +108,11 @@ pub fn value_trait(args: TokenStream, input: TokenStream) -> TokenStream { let mut inline_signature = sig.clone(); inline_signature.ident = inline_function_ident; - let native_function = - NativeFn::new(&format!("{trait_ident}::{ident}"), &inline_function_path); + let native_function = NativeFn::new( + &format!("{trait_ident}::{ident}"), + &inline_function_path, + turbo_fn.is_method(), + ); let native_function_ident = get_trait_default_impl_function_ident(trait_ident, ident); let native_function_ty = native_function.ty(); diff --git a/crates/turbo-tasks-memory/src/memory_backend.rs b/crates/turbo-tasks-memory/src/memory_backend.rs index ac002ca4510e3..099b74d566e98 100644 --- a/crates/turbo-tasks-memory/src/memory_backend.rs +++ b/crates/turbo-tasks-memory/src/memory_backend.rs @@ -539,10 +539,12 @@ impl Backend for MemoryBackend { self.task_statistics().map(|stats| match &*task_type { PersistentTaskType::ResolveNative { fn_type: function_id, + this: _, args: _, } | PersistentTaskType::Native { fn_type: function_id, + this: _, args: _, } => { stats.increment_cache_hit(*function_id); @@ -550,9 +552,10 @@ impl Backend for MemoryBackend { PersistentTaskType::ResolveTrait { trait_type, method_name: name, - args: inputs, + this, + args: _, } => { - // HACK: Resolve the first argument (`self`) in order to attribute the cache hit + // HACK: Resolve the this argument (`self`) in order to attribute the cache hit // to the concrete trait implementation, rather than the dynamic trait method. // This ensures cache hits and misses are both attributed to the same thing. // @@ -567,10 +570,7 @@ impl Backend for MemoryBackend { // ResolveTrait tasks. let trait_type = *trait_type; let name = name.clone(); - let this = inputs - .first() - .cloned() - .expect("No arguments for trait call"); + let this = *this; let stats = Arc::clone(stats); turbo_tasks.run_once(Box::pin(async move { let function_id = @@ -586,6 +586,7 @@ impl Backend for MemoryBackend { self.task_statistics().map(|stats| match &*task_type { PersistentTaskType::Native { fn_type: function_id, + this: _, args: _, } => { stats.increment_cache_miss(*function_id); diff --git a/crates/turbo-tasks-memory/src/task.rs b/crates/turbo-tasks-memory/src/task.rs index b56c4dae65f76..c7f5863390a01 100644 --- a/crates/turbo-tasks-memory/src/task.rs +++ b/crates/turbo-tasks-memory/src/task.rs @@ -557,12 +557,14 @@ impl Task { TaskTypeForDescription::Persistent(ty) => match &***ty { PersistentTaskType::Native { fn_type: native_fn, + this: _, args: _, } => { format!("[{}] {}", id, registry::get_function(*native_fn).name) } PersistentTaskType::ResolveNative { fn_type: native_fn, + this: _, args: _, } => { format!( @@ -574,6 +576,7 @@ impl Task { PersistentTaskType::ResolveTrait { trait_type, method_name: fn_name, + this: _, args: _, } => { format!( @@ -722,18 +725,20 @@ impl Task { TaskType::Persistent { ty, .. } => match &***ty { PersistentTaskType::Native { fn_type: native_fn, + this, args: inputs, } => { let func = registry::get_function(*native_fn); let span = func.span(); let entered = span.enter(); - let bound_fn = func.bind(inputs); + let bound_fn = func.bind(*this, inputs); let future = bound_fn(); drop(entered); (future, span) } PersistentTaskType::ResolveNative { fn_type: ref native_fn_id, + this, args: inputs, } => { let native_fn_id = *native_fn_id; @@ -744,6 +749,7 @@ impl Task { let turbo_tasks = turbo_tasks.pin(); let future = Box::pin(PersistentTaskType::run_resolve_native( native_fn_id, + *this, inputs, turbo_tasks, )); @@ -753,6 +759,7 @@ impl Task { PersistentTaskType::ResolveTrait { trait_type: trait_type_id, method_name: name, + this, args: inputs, } => { let trait_type_id = *trait_type_id; @@ -765,6 +772,7 @@ impl Task { let future = Box::pin(PersistentTaskType::run_resolve_trait( trait_type_id, name, + *this, inputs, turbo_tasks, )); diff --git a/crates/turbo-tasks-memory/tests/call_types.rs b/crates/turbo-tasks-memory/tests/call_types.rs new file mode 100644 index 0000000000000..c5e2d7f3168ea --- /dev/null +++ b/crates/turbo-tasks-memory/tests/call_types.rs @@ -0,0 +1,184 @@ +#![feature(arbitrary_self_types)] + +use anyhow::Result; +use turbo_tasks::Vc; +use turbo_tasks_testing::{register, run}; + +register!(); + +#[tokio::test] +async fn functions() { + run! { + assert_eq!(*fn_plain().await?, 42); + assert_eq!(*fn_arg(43).await?, 43); + assert_eq!(*fn_vc_arg(Vc::cell(44)).await?, 44); + assert_eq!(*async_fn_plain().await?, 42); + assert_eq!(*async_fn_arg(43).await?, 43); + assert_eq!(*async_fn_vc_arg(Vc::cell(44)).await?, 44); + } +} + +#[turbo_tasks::function] +fn fn_plain() -> Vc { + Vc::cell(42) +} + +#[turbo_tasks::function] +fn fn_arg(n: u32) -> Vc { + Vc::cell(n) +} + +#[turbo_tasks::function] +fn fn_vc_arg(n: Vc) -> Vc { + n +} + +#[turbo_tasks::function] +async fn async_fn_plain() -> Result> { + Ok(Vc::cell(42)) +} + +#[turbo_tasks::function] +async fn async_fn_arg(n: u32) -> Result> { + Ok(Vc::cell(n)) +} + +#[turbo_tasks::function] +async fn async_fn_vc_arg(n: Vc) -> Result> { + Ok(Vc::cell(*n.await?)) +} + +#[tokio::test] +async fn methods() { + run! { + assert_eq!(*Value::static_method().await?, 42); + assert_eq!(*Value::async_static_method().await?, 42); + + let value = Value(43).cell(); + assert_eq!(*value.method().await?, 43); + assert_eq!(*value.async_method().await?, 43); + assert_eq!(*value.vc_method().await?, 42); + assert_eq!(*value.async_vc_method().await?, 43); + } +} + +#[turbo_tasks::value] +struct Value(u32); + +#[turbo_tasks::value_impl] +impl Value { + #[turbo_tasks::function] + fn static_method() -> Vc { + Vc::cell(42) + } + + #[turbo_tasks::function] + async fn async_static_method() -> Result> { + Ok(Vc::cell(42)) + } + + #[turbo_tasks::function] + fn method(&self) -> Vc { + Vc::cell(self.0) + } + + #[turbo_tasks::function] + async fn async_method(&self) -> Result> { + Ok(Vc::cell(self.0)) + } + + #[turbo_tasks::function] + fn vc_method(self: Vc) -> Vc { + Vc::cell(42) + } + + #[turbo_tasks::function] + async fn async_vc_method(self: Vc) -> Result> { + Ok(Vc::cell(self.await?.0)) + } +} + +#[tokio::test] +async fn trait_methods() { + run! { + assert_eq!(*Value::static_trait_method().await?, 42); + assert_eq!(*Value::async_static_trait_method().await?, 42); + + let value = Value(43).cell(); + assert_eq!(*value.trait_method().await?, 43); + assert_eq!(*value.async_trait_method().await?, 43); + assert_eq!(*value.default_trait_method().await?, 42); + assert_eq!(*value.default_async_trait_method().await?, 42); + + let trait_value: Vc> = Vc::upcast(value); + assert_eq!(*trait_value.trait_method().await?, 43); + assert_eq!(*trait_value.async_trait_method().await?, 43); + assert_eq!(*trait_value.default_trait_method().await?, 42); + assert_eq!(*trait_value.default_async_trait_method().await?, 42); + + let value = wrap_value(value); + assert_eq!(*value.trait_method().await?, 43); + assert_eq!(*value.async_trait_method().await?, 43); + assert_eq!(*value.default_trait_method().await?, 42); + assert_eq!(*value.default_async_trait_method().await?, 42); + + let trait_value = wrap_trait_value(trait_value); + assert_eq!(*trait_value.trait_method().await?, 43); + assert_eq!(*trait_value.async_trait_method().await?, 43); + assert_eq!(*trait_value.default_trait_method().await?, 42); + assert_eq!(*trait_value.default_async_trait_method().await?, 42); + } +} + +#[turbo_tasks::function] +fn wrap_value(v: Vc) -> Vc { + v +} + +#[turbo_tasks::function] +fn wrap_trait_value(v: Vc>) -> Vc> { + v +} + +#[turbo_tasks::value_trait] +trait ValueTrait { + fn static_trait_method() -> Vc; + async fn async_static_trait_method() -> Result>; + fn default_static_trait_method() -> Vc { + Vc::cell(42) + } + async fn default_async_static_trait_method() -> Result> { + Ok(Vc::cell(42)) + } + fn trait_method(&self) -> Vc; + fn async_trait_method(&self) -> Result>; + fn default_trait_method(self: Vc) -> Vc { + Vc::cell(42) + } + async fn default_async_trait_method(self: Vc) -> Result> { + Ok(Vc::cell(42)) + } +} + +#[turbo_tasks::value_impl] +impl ValueTrait for Value { + #[turbo_tasks::function] + fn static_trait_method() -> Vc { + Vc::cell(42) + } + + #[turbo_tasks::function] + async fn async_static_trait_method() -> Result> { + Ok(Vc::cell(42)) + } + + #[turbo_tasks::function] + fn trait_method(&self) -> Vc { + Vc::cell(self.0) + } + + #[turbo_tasks::function] + async fn async_trait_method(&self) -> Result> { + Ok(Vc::cell(self.0)) + } +} diff --git a/crates/turbo-tasks-testing/src/lib.rs b/crates/turbo-tasks-testing/src/lib.rs index da406efdfb645..7224e7db52fc9 100644 --- a/crates/turbo-tasks-testing/src/lib.rs +++ b/crates/turbo-tasks-testing/src/lib.rs @@ -36,14 +36,15 @@ pub struct VcStorage { tasks: Mutex>, } -impl TurboTasksCallApi for VcStorage { +impl VcStorage { fn dynamic_call( &self, func: turbo_tasks::FunctionId, + this_arg: Option, inputs: Vec, ) -> RawVc { let this = self.this.upgrade().unwrap(); - let func = registry::get_function(func).bind(&inputs); + let func = registry::get_function(func).bind(this_arg, &inputs); let handle = tokio::runtime::Handle::current(); let future = func(); let i = { @@ -77,6 +78,25 @@ impl TurboTasksCallApi for VcStorage { })); RawVc::TaskOutput(id) } +} + +impl TurboTasksCallApi for VcStorage { + fn dynamic_call( + &self, + func: turbo_tasks::FunctionId, + inputs: Vec, + ) -> RawVc { + self.dynamic_call(func, None, inputs) + } + + fn dynamic_this_call( + &self, + func: turbo_tasks::FunctionId, + this_arg: RawVc, + inputs: Vec, + ) -> RawVc { + self.dynamic_call(func, Some(this_arg), inputs) + } fn native_call( &self, @@ -86,10 +106,20 @@ impl TurboTasksCallApi for VcStorage { unreachable!() } + fn this_call( + &self, + _func: turbo_tasks::FunctionId, + _this: RawVc, + _inputs: Vec, + ) -> RawVc { + unreachable!() + } + fn trait_call( &self, _trait_type: turbo_tasks::TraitTypeId, _trait_fn_name: Cow<'static, str>, + _this: RawVc, _inputs: Vec, ) -> RawVc { unreachable!() diff --git a/crates/turbo-tasks/src/backend.rs b/crates/turbo-tasks/src/backend.rs index 38a81bf50042c..ba0604a44c115 100644 --- a/crates/turbo-tasks/src/backend.rs +++ b/crates/turbo-tasks/src/backend.rs @@ -17,9 +17,13 @@ use tracing::Span; pub use crate::id::BackendJobId; use crate::{ - event::EventListener, manager::TurboTasksBackendApi, raw_vc::CellId, registry, + event::EventListener, + manager::TurboTasksBackendApi, + raw_vc::CellId, + registry, + trait_helpers::{get_trait_method, has_trait, traits}, ConcreteTaskInput, FunctionId, RawVc, ReadRef, SharedReference, TaskId, TaskIdProvider, - TaskIdSet, TraitRef, TraitTypeId, VcValueTrait, VcValueType, + TaskIdSet, TraitRef, TraitTypeId, ValueTypeId, VcValueTrait, VcValueType, }; pub enum TaskType { @@ -66,6 +70,7 @@ pub enum PersistentTaskType { /// A normal task execution a native (rust) function Native { fn_type: FunctionId, + this: Option, args: Vec, }, @@ -73,6 +78,7 @@ pub enum PersistentTaskType { /// resolve arguments. The inner function call will do a cache lookup. ResolveNative { fn_type: FunctionId, + this: Option, args: Vec, }, @@ -83,6 +89,7 @@ pub enum PersistentTaskType { ResolveTrait { trait_type: TraitTypeId, method_name: Cow<'static, str>, + this: RawVc, args: Vec, }, } @@ -98,15 +105,18 @@ impl PersistentTaskType { match self { Self::Native { fn_type: _, + this: _, args: inputs, } => inputs.shrink_to_fit(), Self::ResolveNative { fn_type: _, + this: _, args: inputs, } => inputs.shrink_to_fit(), Self::ResolveTrait { trait_type: _, method_name: _, + this: _, args: inputs, } => inputs.shrink_to_fit(), } @@ -116,15 +126,18 @@ impl PersistentTaskType { match self { PersistentTaskType::Native { fn_type: _, + this: _, args: v, } | PersistentTaskType::ResolveNative { fn_type: _, + this: _, args: v, } | PersistentTaskType::ResolveTrait { trait_type: _, method_name: _, + this: _, args: v, } => v.len(), } @@ -134,15 +147,18 @@ impl PersistentTaskType { match self { PersistentTaskType::Native { fn_type: _, + this: _, args: v, } | PersistentTaskType::ResolveNative { fn_type: _, + this: _, args: v, } | PersistentTaskType::ResolveTrait { trait_type: _, method_name: _, + this: _, args: v, } => v.is_empty(), } @@ -152,25 +168,31 @@ impl PersistentTaskType { match self { PersistentTaskType::Native { fn_type: f, + this, args: v, } => PersistentTaskType::Native { fn_type: *f, + this: *this, args: v[..len].to_vec(), }, PersistentTaskType::ResolveNative { fn_type: f, + this, args: v, } => PersistentTaskType::ResolveNative { fn_type: *f, + this: *this, args: v[..len].to_vec(), }, PersistentTaskType::ResolveTrait { trait_type: f, method_name: n, + this, args: v, } => PersistentTaskType::ResolveTrait { trait_type: *f, method_name: n.clone(), + this: *this, args: v[..len].to_vec(), }, } @@ -185,15 +207,18 @@ impl PersistentTaskType { match self { PersistentTaskType::Native { fn_type: native_fn, + this: _, args: _, } | PersistentTaskType::ResolveNative { fn_type: native_fn, + this: _, args: _, } => Cow::Borrowed(®istry::get_function(*native_fn).name), PersistentTaskType::ResolveTrait { trait_type: trait_id, method_name: fn_name, + this: _, args: _, } => format!("{}::{}", registry::get_trait(*trait_id).name, fn_name).into(), } @@ -424,83 +449,82 @@ pub trait Backend: Sync + Send { impl PersistentTaskType { pub async fn run_resolve_native( fn_id: FunctionId, + mut this: Option, mut inputs: Vec, turbo_tasks: Arc>, ) -> Result { - for i in 0..inputs.len() { - let input = unsafe { take(inputs.get_unchecked_mut(i)) }; - let input = input.resolve().await?; - unsafe { - *inputs.get_unchecked_mut(i) = input; - } + if let Some(this) = this.as_mut() { + *this = this.resolve().await?; + } + for input in inputs.iter_mut() { + *input = take(input).resolve().await?; } - Ok(turbo_tasks.native_call(fn_id, inputs)) + Ok(if let Some(this) = this { + turbo_tasks.this_call(fn_id, this, inputs) + } else { + turbo_tasks.native_call(fn_id, inputs) + }) } pub async fn resolve_trait_method( trait_type: TraitTypeId, name: Cow<'static, str>, - this: ConcreteTaskInput, + this: RawVc, ) -> Result { - Self::resolve_trait_method_from_value( - trait_type, - this.resolve().await?.resolve_to_value().await?, - name, - ) + let CellContent(Some(SharedReference(Some(value_type), _))) = this.into_read().await? + else { + bail!("Cell is empty or untyped"); + }; + Self::resolve_trait_method_from_value(trait_type, value_type, name) } pub async fn run_resolve_trait( trait_type: TraitTypeId, name: Cow<'static, str>, - inputs: Vec, + this: RawVc, + mut inputs: Vec, turbo_tasks: Arc>, ) -> Result { - let mut resolved_inputs = Vec::with_capacity(inputs.len()); - let mut iter = inputs.into_iter(); - - let this = iter - .next() - .expect("No arguments for trait call") - .resolve() - .await?; - let this_value = this.clone().resolve_to_value().await?; - - let native_fn = Self::resolve_trait_method_from_value(trait_type, this_value, name)?; - resolved_inputs.push(this); - for input in iter { - resolved_inputs.push(input) + let this = this.resolve().await?; + let CellContent(Some(SharedReference(this_ty, _))) = this.into_read().await? else { + bail!("Cell is empty"); + }; + let Some(this_ty) = this_ty else { + bail!("Cell is untyped"); + }; + + let native_fn = Self::resolve_trait_method_from_value(trait_type, this_ty, name)?; + for input in inputs.iter_mut() { + *input = take(input).resolve().await?; } - Ok(turbo_tasks.dynamic_call(native_fn, resolved_inputs)) + Ok(turbo_tasks.dynamic_this_call(native_fn, this, inputs)) } /// Shared helper used by [`Self::resolve_trait_method`] and /// [`Self::run_resolve_trait`]. fn resolve_trait_method_from_value( trait_type: TraitTypeId, - this_value: ConcreteTaskInput, + value_type: ValueTypeId, name: Cow<'static, str>, ) -> Result { - match this_value.get_trait_method(trait_type, name) { + match get_trait_method(trait_type, value_type, name) { Ok(native_fn) => Ok(native_fn), Err(name) => { - if !this_value.has_trait(trait_type) { - let traits = this_value - .traits() - .iter() - .fold(String::new(), |mut out, t| { - let _ = write!(out, " {}", t); - out - }); + if !has_trait(value_type, trait_type) { + let traits = traits(value_type).iter().fold(String::new(), |mut out, t| { + let _ = write!(out, " {}", t); + out + }); Err(anyhow!( "{} doesn't implement {} (only{})", - this_value, + registry::get_value_type(value_type), registry::get_trait(trait_type), traits, )) } else { Err(anyhow!( "{} implements trait {}, but method {} is missing", - this_value, + registry::get_value_type(value_type), registry::get_trait(trait_type), name )) @@ -516,23 +540,27 @@ impl PersistentTaskType { match self { PersistentTaskType::Native { fn_type: fn_id, + this, args: inputs, } => { let native_fn = registry::get_function(fn_id); - let bound = native_fn.bind(&inputs); + let bound = native_fn.bind(this, &inputs); (bound)() } PersistentTaskType::ResolveNative { fn_type: fn_id, + this, args: inputs, - } => Box::pin(Self::run_resolve_native(fn_id, inputs, turbo_tasks)), + } => Box::pin(Self::run_resolve_native(fn_id, this, inputs, turbo_tasks)), PersistentTaskType::ResolveTrait { trait_type, method_name: name, + this, args: inputs, } => Box::pin(Self::run_resolve_trait( trait_type, name, + this, inputs, turbo_tasks, )), @@ -561,6 +589,7 @@ pub(crate) mod tests { assert_eq!( PersistentTaskType::Native { fn_type: *MOCK_FUNC_TASK_FUNCTION_ID, + this: None, args: Vec::new() } .get_name(), @@ -570,6 +599,7 @@ pub(crate) mod tests { PersistentTaskType::ResolveTrait { trait_type: *MOCKTRAIT_TRAIT_TYPE_ID, method_name: "mock_method_task".into(), + this: RawVc::TaskOutput(unsafe { TaskId::new_unchecked(1) }), args: Vec::new() } .get_name(), diff --git a/crates/turbo-tasks/src/lib.rs b/crates/turbo-tasks/src/lib.rs index 84c107c055aff..b5aa822a019c4 100644 --- a/crates/turbo-tasks/src/lib.rs +++ b/crates/turbo-tasks/src/lib.rs @@ -67,6 +67,7 @@ pub mod small_duration; mod state; pub mod task; pub mod trace; +mod trait_helpers; mod trait_ref; mod triomphe_utils; pub mod util; @@ -88,10 +89,10 @@ pub use invalidation::{ pub use join_iter_ext::{JoinIterExt, TryFlatJoinIterExt, TryJoinIterExt}; pub use keyed_cell::{global_keyed_cell, keyed_cell}; pub use manager::{ - dynamic_call, emit, get_invalidator, mark_finished, mark_stateful, prevent_gc, run_once, - run_once_with_reason, spawn_blocking, spawn_thread, trait_call, turbo_tasks, CurrentCellRef, - Invalidator, TaskIdProvider, TurboTasks, TurboTasksApi, TurboTasksBackendApi, - TurboTasksCallApi, Unused, UpdateInfo, + 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, + turbo_tasks, CurrentCellRef, Invalidator, TaskIdProvider, TurboTasks, TurboTasksApi, + TurboTasksBackendApi, TurboTasksCallApi, Unused, UpdateInfo, }; pub use native_function::NativeFunction; pub use raw_vc::{CellId, RawVc, ReadRawVcFuture, ResolveTypeError}; diff --git a/crates/turbo-tasks/src/manager.rs b/crates/turbo-tasks/src/manager.rs index 28bb8c316126d..d810c1366f061 100644 --- a/crates/turbo-tasks/src/manager.rs +++ b/crates/turbo-tasks/src/manager.rs @@ -30,8 +30,8 @@ use crate::{ id::{BackendJobId, FunctionId, TraitTypeId}, id_factory::IdFactory, raw_vc::{CellId, RawVc}, - registry, trace::TraceRawVcs, + trait_helpers::get_trait_method, util::StaticOrArc, vc::ReadVcFuture, Completion, ConcreteTaskInput, InvalidationReason, InvalidationReasonSet, SharedReference, @@ -40,11 +40,19 @@ use crate::{ pub trait TurboTasksCallApi: Sync + Send { fn dynamic_call(&self, func: FunctionId, inputs: Vec) -> RawVc; + fn dynamic_this_call( + &self, + func: FunctionId, + this: RawVc, + inputs: Vec, + ) -> RawVc; fn native_call(&self, func: FunctionId, inputs: Vec) -> RawVc; + fn this_call(&self, func: FunctionId, this: RawVc, inputs: Vec) -> RawVc; fn trait_call( &self, trait_type: TraitTypeId, trait_fn_name: Cow<'static, str>, + this: RawVc, inputs: Vec, ) -> RawVc; @@ -371,6 +379,26 @@ impl TurboTasks { RawVc::TaskOutput(self.backend.get_or_create_persistent_task( PersistentTaskType::Native { fn_type: func, + this: None, + args: inputs, + }, + current_task("turbo_function calls"), + self, + )) + } + + /// Call a native function with arguments. + /// All inputs must be resolved. + pub(crate) fn this_call( + &self, + func: FunctionId, + this: RawVc, + inputs: Vec, + ) -> RawVc { + RawVc::TaskOutput(self.backend.get_or_create_persistent_task( + PersistentTaskType::Native { + fn_type: func, + this: Some(this), args: inputs, }, current_task("turbo_function calls"), @@ -387,6 +415,30 @@ impl TurboTasks { RawVc::TaskOutput(self.backend.get_or_create_persistent_task( PersistentTaskType::ResolveNative { fn_type: func, + this: None, + args: inputs, + }, + current_task("turbo_function calls"), + self, + )) + } + } + + /// Calls a native function with arguments. Resolves arguments when needed + /// with a wrapper task. + pub fn dynamic_this_call( + &self, + func: FunctionId, + this: RawVc, + inputs: Vec, + ) -> RawVc { + if this.is_resolved() && inputs.iter().all(|i| i.is_resolved()) { + self.this_call(func, this, inputs) + } else { + RawVc::TaskOutput(self.backend.get_or_create_persistent_task( + PersistentTaskType::ResolveNative { + fn_type: func, + this: Some(this), args: inputs, }, current_task("turbo_function calls"), @@ -401,19 +453,21 @@ impl TurboTasks { &self, trait_type: TraitTypeId, mut trait_fn_name: Cow<'static, str>, + this: RawVc, inputs: Vec, ) -> 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 // function - let first_input = inputs.first().expect("trait call without self argument"); - if let &ConcreteTaskInput::TaskCell(_, CellId { type_id, .. }) = first_input { - let value_type = registry::get_value_type(type_id); - let key = (trait_type, trait_fn_name); - if let Some(native_fn) = value_type.get_trait_method(&key) { - return self.dynamic_call(*native_fn, inputs); + if let RawVc::TaskCell(_, CellId { type_id, .. }) = this { + match get_trait_method(trait_type, type_id, trait_fn_name) { + Ok(native_fn) => { + return self.dynamic_this_call(native_fn, this, inputs); + } + Err(name) => { + trait_fn_name = name; + } } - trait_fn_name = key.1; } // create a wrapper task to resolve all inputs @@ -421,6 +475,7 @@ impl TurboTasks { PersistentTaskType::ResolveTrait { trait_type, method_name: trait_fn_name, + this, args: inputs, }, current_task("turbo_function calls"), @@ -804,16 +859,28 @@ impl TurboTasksCallApi for TurboTasks { fn dynamic_call(&self, func: FunctionId, inputs: Vec) -> RawVc { self.dynamic_call(func, inputs) } + fn dynamic_this_call( + &self, + func: FunctionId, + this: RawVc, + inputs: Vec, + ) -> RawVc { + self.dynamic_this_call(func, this, inputs) + } fn native_call(&self, func: FunctionId, inputs: Vec) -> RawVc { self.native_call(func, inputs) } + fn this_call(&self, func: FunctionId, this: RawVc, inputs: Vec) -> RawVc { + self.this_call(func, this, inputs) + } fn trait_call( &self, trait_type: TraitTypeId, trait_fn_name: Cow<'static, str>, + this: RawVc, inputs: Vec, ) -> RawVc { - self.trait_call(trait_type, trait_fn_name, inputs) + self.trait_call(trait_type, trait_fn_name, this, inputs) } #[track_caller] @@ -1292,13 +1359,20 @@ pub fn dynamic_call(func: FunctionId, inputs: Vec) -> RawVc { with_turbo_tasks(|tt| tt.dynamic_call(func, inputs)) } +/// Calls [`TurboTasks::dynamic_this_call`] for the current turbo tasks +/// instance. +pub fn dynamic_this_call(func: FunctionId, this: RawVc, inputs: Vec) -> RawVc { + with_turbo_tasks(|tt| tt.dynamic_this_call(func, this, inputs)) +} + /// Calls [`TurboTasks::trait_call`] for the current turbo tasks instance. pub fn trait_call( trait_type: TraitTypeId, trait_fn_name: Cow<'static, str>, + this: RawVc, inputs: Vec, ) -> RawVc { - with_turbo_tasks(|tt| tt.trait_call(trait_type, trait_fn_name, inputs)) + with_turbo_tasks(|tt| tt.trait_call(trait_type, trait_fn_name, this, inputs)) } pub fn turbo_tasks() -> Arc { diff --git a/crates/turbo-tasks/src/native_function.rs b/crates/turbo-tasks/src/native_function.rs index 1a17956222596..c43910288fc99 100644 --- a/crates/turbo-tasks/src/native_function.rs +++ b/crates/turbo-tasks/src/native_function.rs @@ -3,10 +3,14 @@ use std::{fmt::Debug, hash::Hash}; use tracing::Span; use crate::{ + self as turbo_tasks, registry::register_function, - task::{function::NativeTaskFn, IntoTaskFn, TaskFn}, + task::{ + function::{IntoTaskFnWithThis, NativeTaskFn}, + IntoTaskFn, TaskFn, + }, util::SharedError, - ConcreteTaskInput, {self as turbo_tasks}, + ConcreteTaskInput, RawVc, }; /// A native (rust) turbo-tasks function. It's used internally by @@ -30,7 +34,7 @@ impl Debug for NativeFunction { } impl NativeFunction { - pub fn new(name: String, implementation: I) -> Self + pub fn new_function(name: String, implementation: I) -> Self where I: IntoTaskFn, { @@ -40,9 +44,19 @@ impl NativeFunction { } } + pub fn new_method(name: String, implementation: I) -> Self + where + I: IntoTaskFnWithThis, + { + Self { + name, + implementation: Box::new(implementation.into_task_fn_with_this()), + } + } + /// Creates a functor for execution from a fixed set of inputs. - pub fn bind(&'static self, inputs: &[ConcreteTaskInput]) -> NativeTaskFn { - match (self.implementation).functor(inputs) { + pub fn bind(&'static self, this: Option, inputs: &[ConcreteTaskInput]) -> NativeTaskFn { + match (self.implementation).functor(this, inputs) { Ok(functor) => functor, Err(err) => { let err = SharedError::new(err); diff --git a/crates/turbo-tasks/src/raw_vc.rs b/crates/turbo-tasks/src/raw_vc.rs index e36b2894626ea..50c4a8ed16c3f 100644 --- a/crates/turbo-tasks/src/raw_vc.rs +++ b/crates/turbo-tasks/src/raw_vc.rs @@ -59,6 +59,13 @@ pub enum RawVc { } impl RawVc { + pub(crate) fn is_resolved(&self) -> bool { + match self { + RawVc::TaskOutput(_) => false, + RawVc::TaskCell(_, _) => true, + } + } + pub(crate) fn into_read(self) -> ReadRawVcFuture { // returns a custom future to have something concrete and sized // this avoids boxing in IntoFuture diff --git a/crates/turbo-tasks/src/task/concrete_task_input.rs b/crates/turbo-tasks/src/task/concrete_task_input.rs index 8f0a94693c918..e0982858c744a 100644 --- a/crates/turbo-tasks/src/task/concrete_task_input.rs +++ b/crates/turbo-tasks/src/task/concrete_task_input.rs @@ -1,6 +1,5 @@ use std::{ any::Any, - borrow::Cow, fmt::{Debug, Display}, future::Future, hash::Hash, @@ -14,12 +13,11 @@ use unsize::CoerceUnsize; use crate::{ backend::CellContent, - id::{FunctionId, TraitTypeId}, 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, TraitType, ValueTypeId, + turbo_tasks, CellId, RawVc, RcStr, TaskId, ValueTypeId, }; /// A type-erased wrapper for [`triomphe::Arc`]. @@ -405,74 +403,6 @@ impl ConcreteTaskInput { } } - pub fn get_trait_method( - &self, - trait_type: TraitTypeId, - name: Cow<'static, str>, - ) -> Result> { - match self { - ConcreteTaskInput::TaskOutput(_) | ConcreteTaskInput::TaskCell(_, _) => { - panic!("get_trait_method must be called on a resolved TaskInput") - } - ConcreteTaskInput::SharedValue(SharedValue(ty, _)) - | ConcreteTaskInput::SharedReference(SharedReference(ty, _)) => { - if let Some(ty) = *ty { - let key = (trait_type, name); - if let Some(func) = registry::get_value_type(ty).get_trait_method(&key) { - Ok(*func) - } else if let Some(func) = registry::get_trait(trait_type) - .default_trait_methods - .get(&key.1) - { - Ok(*func) - } else { - Err(key.1) - } - } else { - Err(name) - } - } - _ => Err(name), - } - } - - pub fn has_trait(&self, trait_type: TraitTypeId) -> bool { - match self { - ConcreteTaskInput::TaskOutput(_) | ConcreteTaskInput::TaskCell(_, _) => { - panic!("has_trait() must be called on a resolved TaskInput") - } - ConcreteTaskInput::SharedValue(SharedValue(ty, _)) - | ConcreteTaskInput::SharedReference(SharedReference(ty, _)) => { - if let Some(ty) = *ty { - registry::get_value_type(ty).has_trait(&trait_type) - } else { - false - } - } - _ => false, - } - } - - pub fn traits(&self) -> Vec<&'static TraitType> { - match self { - ConcreteTaskInput::TaskOutput(_) | ConcreteTaskInput::TaskCell(_, _) => { - panic!("traits() must be called on a resolved TaskInput") - } - ConcreteTaskInput::SharedValue(SharedValue(ty, _)) - | ConcreteTaskInput::SharedReference(SharedReference(ty, _)) => { - if let Some(ty) = *ty { - registry::get_value_type(ty) - .traits_iter() - .map(registry::get_trait) - .collect() - } else { - Vec::new() - } - } - _ => Vec::new(), - } - } - pub fn is_resolved(&self) -> bool { match self { ConcreteTaskInput::TaskOutput(_) => false, diff --git a/crates/turbo-tasks/src/task/function.rs b/crates/turbo-tasks/src/task/function.rs index c1bfcaac41e39..aa436bf6cef39 100644 --- a/crates/turbo-tasks/src/task/function.rs +++ b/crates/turbo-tasks/src/task/function.rs @@ -32,7 +32,7 @@ pub type NativeTaskFuture = Pin> + Send>>; pub type NativeTaskFn = Box NativeTaskFuture + Send + Sync>; pub trait TaskFn: Send + Sync + 'static { - fn functor(&self, inputs: &[ConcreteTaskInput]) -> Result; + fn functor(&self, this: Option, inputs: &[ConcreteTaskInput]) -> Result; } pub trait IntoTaskFn { @@ -58,6 +58,29 @@ where } } +pub trait IntoTaskFnWithThis { + type TaskFn: TaskFn; + + fn into_task_fn_with_this(self) -> Self::TaskFn; +} + +impl IntoTaskFnWithThis for F +where + F: TaskFnInputFunctionWithThis, + Mode: TaskFnMode, + Inputs: TaskInputs, +{ + type TaskFn = FunctionTaskFnWithThis; + + fn into_task_fn_with_this(self) -> Self::TaskFn { + FunctionTaskFnWithThis { + task_fn: self, + mode: PhantomData, + inputs: PhantomData, + } + } +} + pub struct FunctionTaskFn { task_fn: F, mode: PhantomData, @@ -70,13 +93,36 @@ where Mode: TaskFnMode, Inputs: TaskInputs, { - fn functor(&self, inputs: &[ConcreteTaskInput]) -> Result { - TaskFnInputFunction::functor(&self.task_fn, inputs) + fn functor(&self, this: Option, inputs: &[ConcreteTaskInput]) -> Result { + TaskFnInputFunction::functor(&self.task_fn, this, inputs) + } +} + +pub struct FunctionTaskFnWithThis { + task_fn: F, + mode: PhantomData, + inputs: PhantomData, +} + +impl TaskFn for FunctionTaskFnWithThis +where + F: TaskFnInputFunctionWithThis, + Mode: TaskFnMode, + Inputs: TaskInputs, +{ + fn functor(&self, this: Option, inputs: &[ConcreteTaskInput]) -> Result { + TaskFnInputFunctionWithThis::functor(&self.task_fn, this, inputs) } } trait TaskFnInputFunction: Send + Sync + Clone + 'static { - fn functor(&self, inputs: &[ConcreteTaskInput]) -> Result; + fn functor(&self, this: Option, inputs: &[ConcreteTaskInput]) -> Result; +} + +trait TaskFnInputFunctionWithThis: + Send + Sync + Clone + 'static +{ + fn functor(&self, this: Option, inputs: &[ConcreteTaskInput]) -> Result; } pub trait TaskInputs: Send + Sync + 'static {} @@ -91,12 +137,12 @@ pub trait TaskFnMode: Send + Sync + 'static {} pub struct FunctionMode; impl TaskFnMode for FunctionMode {} -pub struct MethodMode; -impl TaskFnMode for MethodMode {} - pub struct AsyncFunctionMode; impl TaskFnMode for AsyncFunctionMode {} +pub struct MethodMode; +impl TaskFnMode for MethodMode {} + pub struct AsyncMethodMode; impl TaskFnMode for AsyncMethodMode {} @@ -128,14 +174,6 @@ macro_rules! task_fn_impl { get_args_iter(inputs.iter()) } - pub fn get_method_args( - inputs: &[ConcreteTaskInput], - ) -> Result<(&ConcreteTaskInput, ($(&as_concrete_task_input!($arg),)*))> { - let mut iter = inputs.iter(); - let recv = iter.next().context("task is missing receiver")?; - Ok((recv, get_args_iter(iter)?)) - } - fn get_args_iter( mut iter: std::slice::Iter<'_, ConcreteTaskInput>, ) -> Result<($(&as_concrete_task_input!($arg),)*)> { @@ -154,7 +192,7 @@ macro_rules! task_fn_impl { Output: TaskOutput + 'static, { #[allow(non_snake_case)] - fn functor(&self, inputs: &[ConcreteTaskInput]) -> Result { + fn functor(&self, _this: Option, inputs: &[ConcreteTaskInput]) -> Result { let ($($arg,)*) = $helper_module::get_args(inputs)?; let task_fn = self.clone(); @@ -183,7 +221,7 @@ macro_rules! task_fn_impl { Output: TaskOutput + 'static, { #[allow(non_snake_case)] - fn functor(&self, inputs: &[ConcreteTaskInput]) -> Result { + fn functor(&self, _this: Option, inputs: &[ConcreteTaskInput]) -> Result { let ($($arg,)*) = $helper_module::get_args(inputs)?; let task_fn = self.clone(); @@ -204,7 +242,7 @@ macro_rules! task_fn_impl { } } - impl TaskFnInputFunction, $($arg,)*)> for F + impl TaskFnInputFunctionWithThis, $($arg,)*)> for F where Recv: VcValueType, $($arg: TaskInput + 'static,)* @@ -212,18 +250,17 @@ macro_rules! task_fn_impl { Output: TaskOutput + 'static, { #[allow(non_snake_case)] - fn functor(&self, inputs: &[ConcreteTaskInput]) -> Result { - let (recv, ($($arg,)*)) = $helper_module::get_method_args(inputs)?; - + fn functor(&self, this: Option, inputs: &[ConcreteTaskInput]) -> Result { let task_fn = self.clone(); - let recv = Vc::::try_from_concrete(recv)?; + let recv = Vc::::from(this.expect("Method need to have a `self` argument")); + + let ($($arg,)*) = $helper_module::get_args(inputs)?; $( let $arg = $arg::try_from_concrete($arg)?; )* Ok(Box::new(move || { let task_fn = task_fn.clone(); - let recv = recv.clone(); $( let $arg = $arg.clone(); )* @@ -237,6 +274,36 @@ macro_rules! task_fn_impl { } } + impl TaskFnInputFunctionWithThis, $($arg,)*)> for F + where + Recv: 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, inputs: &[ConcreteTaskInput]) -> Result { + let task_fn = self.clone(); + let recv = Vc::::from(this.expect("Method need to have a `self` argument")); + + let ($($arg,)*) = $helper_module::get_args(inputs)?; + $( + let $arg = $arg::try_from_concrete($arg)?; + )* + + Ok(Box::new(move || { + let task_fn = task_fn.clone(); + $( + let $arg = $arg.clone(); + )* + + Box::pin(async move { + Output::try_into_raw_vc((task_fn)(recv, $($arg),*)) + }) + })) + } + } + pub trait $async_fn_trait: Fn(A0, $($arg,)*) -> Self::OutputFuture { type OutputFuture: Future>::Output> + Send; type Output: TaskOutput; @@ -252,42 +319,61 @@ macro_rules! task_fn_impl { type Output = Fut::Output; } - impl TaskFnInputFunction, $($arg,)*)> for F + impl TaskFnInputFunctionWithThis, $($arg,)*)> 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, inputs: &[ConcreteTaskInput]) -> Result { + fn functor(&self, this: Option, inputs: &[ConcreteTaskInput]) -> Result { let task_fn = self.clone(); - let mut iter = inputs.iter(); + let recv = Vc::::from(this.expect("Method need to have a `self` argument")); - let recv = iter.next().context("task is missing receiver")?; + let ($($arg,)*) = $helper_module::get_args(inputs)?; $( - let $arg = next_arg(&mut iter, stringify!($arg))?; + let $arg = $arg::try_from_concrete($arg)?; )* - if iter.next().is_some() { - bail!("task was called with too many arguments"); - } + 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) + }) + })) + } + } + + impl TaskFnInputFunctionWithThis, $($arg,)*)> for F + where + Recv: Send + 'static, + $($arg: TaskInput + 'static,)* + F: $async_fn_trait, $($arg,)*> + Clone + Send + Sync + 'static, + { + #[allow(non_snake_case)] + fn functor(&self, this: Option, inputs: &[ConcreteTaskInput]) -> Result { + let task_fn = self.clone(); + let recv = Vc::::from(this.expect("Method need to have a `self` argument")); - let recv = Vc::::try_from_concrete(recv)?; + let ($($arg,)*) = $helper_module::get_args(inputs)?; $( let $arg = $arg::try_from_concrete($arg)?; )* Ok(Box::new(move || { let task_fn = task_fn.clone(); - let recv = recv.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) + , $($arg,)*>>::Output::try_into_raw_vc((task_fn)(recv, $($arg),*).await) }) })) } @@ -439,19 +525,19 @@ mod tests { accepts_task_fn(one_arg.into_task_fn()); let _task_fn = async_one_arg.into_task_fn(); accepts_task_fn(async_one_arg.into_task_fn()); - let task_fn = with_recv.into_task_fn(); + let task_fn = with_recv.into_task_fn_with_this(); accepts_task_fn(task_fn); - let task_fn = async_with_recv.into_task_fn(); + let task_fn = async_with_recv.into_task_fn_with_this(); accepts_task_fn(task_fn); - let task_fn = with_recv_and_str.into_task_fn(); + let task_fn = with_recv_and_str.into_task_fn_with_this(); accepts_task_fn(task_fn); - let task_fn = async_with_recv_and_str.into_task_fn(); + let task_fn = async_with_recv_and_str.into_task_fn_with_this(); accepts_task_fn(task_fn); - let task_fn = async_with_recv_and_str_and_result.into_task_fn(); + let task_fn = async_with_recv_and_str_and_result.into_task_fn_with_this(); accepts_task_fn(task_fn); - let task_fn = ::async_method.into_task_fn(); + let task_fn = ::async_method.into_task_fn_with_this(); accepts_task_fn(task_fn); - let task_fn = Struct::inherent_method.into_task_fn(); + let task_fn = Struct::inherent_method.into_task_fn_with_this(); accepts_task_fn(task_fn); /* diff --git a/crates/turbo-tasks/src/task/mod.rs b/crates/turbo-tasks/src/task/mod.rs index 067a16429d9a0..2fa600f9a502a 100644 --- a/crates/turbo-tasks/src/task/mod.rs +++ b/crates/turbo-tasks/src/task/mod.rs @@ -4,8 +4,6 @@ pub(crate) mod task_input; pub(crate) mod task_output; pub use concrete_task_input::ConcreteTaskInput; -pub use function::{ - AsyncFunctionMode, AsyncMethodMode, FunctionMode, IntoTaskFn, MethodMode, NativeTaskFn, TaskFn, -}; +pub use function::{AsyncFunctionMode, FunctionMode, IntoTaskFn, NativeTaskFn, TaskFn}; pub use task_input::TaskInput; pub use task_output::TaskOutput; diff --git a/crates/turbo-tasks/src/trait_helpers.rs b/crates/turbo-tasks/src/trait_helpers.rs new file mode 100644 index 0000000000000..370443acac31e --- /dev/null +++ b/crates/turbo-tasks/src/trait_helpers.rs @@ -0,0 +1,32 @@ +use std::borrow::Cow; + +use crate::{registry, FunctionId, TraitType, TraitTypeId, ValueTypeId}; + +pub fn get_trait_method( + trait_type: TraitTypeId, + value_type: ValueTypeId, + name: Cow<'static, str>, +) -> Result> { + let key = (trait_type, name); + 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 + .get(&key.1) + { + Ok(*func) + } else { + Err(key.1) + } +} + +pub fn has_trait(value_type: ValueTypeId, trait_type: TraitTypeId) -> bool { + registry::get_value_type(value_type).has_trait(&trait_type) +} + +pub fn traits(value_type: ValueTypeId) -> Vec<&'static TraitType> { + registry::get_value_type(value_type) + .traits_iter() + .map(registry::get_trait) + .collect() +} diff --git a/crates/turbo-tasks/src/value_type.rs b/crates/turbo-tasks/src/value_type.rs index 17b93376da928..4d0301aacc2a1 100644 --- a/crates/turbo-tasks/src/value_type.rs +++ b/crates/turbo-tasks/src/value_type.rs @@ -79,6 +79,12 @@ impl Debug for ValueType { } } +impl Display for ValueType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(&self.name) + } +} + pub fn any_as_serialize( this: &(dyn Any + Send + Sync), ) -> &dyn erased_serde::Serialize {