From a9e24d8b0d4c5dbd5cd57ba8d3059f858c9d75bb Mon Sep 17 00:00:00 2001 From: Jack Rickard Date: Sun, 28 Aug 2022 13:43:14 +0100 Subject: [PATCH 1/2] Allow "constant" tracked functions This adds support for tracked functions with only a database as input, that is, it does not take a salsa struct. --- .../salsa-2022-macros/src/tracked_fn.rs | 13 +++++---- components/salsa-2022/src/salsa_struct.rs | 4 +++ salsa-2022-tests/tests/tracked_fn_constant.rs | 29 +++++++++++++++++++ 3 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 salsa-2022-tests/tests/tracked_fn_constant.rs diff --git a/components/salsa-2022-macros/src/tracked_fn.rs b/components/salsa-2022-macros/src/tracked_fn.rs index 274a673a..98fbeaa2 100644 --- a/components/salsa-2022-macros/src/tracked_fn.rs +++ b/components/salsa-2022-macros/src/tracked_fn.rs @@ -11,10 +11,10 @@ pub(crate) fn tracked_fn( mut item_fn: syn::ItemFn, ) -> syn::Result { let args: FnArgs = syn::parse(args)?; - if item_fn.sig.inputs.len() <= 1 { + if item_fn.sig.inputs.is_empty() { return Err(syn::Error::new( item_fn.sig.ident.span(), - "tracked functions must have at least a database and salsa struct argument", + "tracked functions must have at least a database argument", )); } @@ -332,16 +332,19 @@ fn requires_interning(item_fn: &syn::ItemFn) -> bool { /// Every tracked fn takes a salsa struct as its second argument. /// This fn returns the type of that second argument. -fn salsa_struct_ty(item_fn: &syn::ItemFn) -> &syn::Type { +fn salsa_struct_ty(item_fn: &syn::ItemFn) -> syn::Type { + if item_fn.sig.inputs.len() == 1 { + return parse_quote! { () }; + } match &item_fn.sig.inputs[1] { syn::FnArg::Receiver(_) => panic!("receiver not expected"), - syn::FnArg::Typed(pat_ty) => &pat_ty.ty, + syn::FnArg::Typed(pat_ty) => (*pat_ty.ty).clone(), } } fn fn_configuration(args: &FnArgs, item_fn: &syn::ItemFn) -> Configuration { let jar_ty = args.jar_ty(); - let salsa_struct_ty = salsa_struct_ty(item_fn).clone(); + let salsa_struct_ty = salsa_struct_ty(item_fn); let key_ty = if requires_interning(item_fn) { parse_quote!(salsa::id::Id) } else { diff --git a/components/salsa-2022/src/salsa_struct.rs b/components/salsa-2022/src/salsa_struct.rs index febdae4a..392f0134 100644 --- a/components/salsa-2022/src/salsa_struct.rs +++ b/components/salsa-2022/src/salsa_struct.rs @@ -3,3 +3,7 @@ use crate::{Database, IngredientIndex}; pub trait SalsaStructInDb { fn register_dependent_fn(db: &DB, index: IngredientIndex); } + +impl SalsaStructInDb for () { + fn register_dependent_fn(_db: &DB, _index: IngredientIndex) {} +} diff --git a/salsa-2022-tests/tests/tracked_fn_constant.rs b/salsa-2022-tests/tests/tracked_fn_constant.rs new file mode 100644 index 00000000..19f5d57c --- /dev/null +++ b/salsa-2022-tests/tests/tracked_fn_constant.rs @@ -0,0 +1,29 @@ +//! Test that a constant `tracked` fn (has no inputs) +//! compiles and executes successfully. +#![allow(warnings)] + +#[salsa::jar(db = Db)] +struct Jar(tracked_fn); + +trait Db: salsa::DbWithJar {} + +#[salsa::tracked(jar = Jar)] +fn tracked_fn(db: &dyn Db) -> u32 { + 44 +} + +#[test] +fn execute() { + #[salsa::db(Jar)] + #[derive(Default)] + struct Database { + storage: salsa::Storage, + } + + impl salsa::Database for Database {} + + impl Db for Database {} + + let mut db = Database::default(); + assert_eq!(tracked_fn(&db), 44); +} From 5832ad30901f169e62d2b5c23e3809f2102343d6 Mon Sep 17 00:00:00 2001 From: Jack Rickard Date: Sat, 3 Sep 2022 16:55:31 +0100 Subject: [PATCH 2/2] Replace () with Singleton Salsa struct --- .../salsa-2022-macros/src/tracked_fn.rs | 92 ++++++++++++------- components/salsa-2022/src/salsa_struct.rs | 8 +- salsa-2022-tests/tests/tracked_fn_constant.rs | 2 +- 3 files changed, 65 insertions(+), 37 deletions(-) diff --git a/components/salsa-2022-macros/src/tracked_fn.rs b/components/salsa-2022-macros/src/tracked_fn.rs index 98fbeaa2..58f047ba 100644 --- a/components/salsa-2022-macros/src/tracked_fn.rs +++ b/components/salsa-2022-macros/src/tracked_fn.rs @@ -26,7 +26,7 @@ pub(crate) fn tracked_fn( } if let Some(s) = &args.specify { - if requires_interning(&item_fn) { + if function_type(&item_fn) == FunctionType::RequiresInterning { return Err(syn::Error::new( s.span(), "tracked function takes too many arguments to have its value set with `specify`", @@ -309,11 +309,17 @@ fn configuration_struct(item_fn: &syn::ItemFn) -> syn::ItemStruct { let visibility = &item_fn.vis; let salsa_struct_ty = salsa_struct_ty(item_fn); - let intern_map: syn::Type = if requires_interning(item_fn) { - let key_ty = key_tuple_ty(item_fn); - parse_quote! { salsa::interned::InternedIngredient } - } else { - parse_quote! { salsa::interned::IdentityInterner<#salsa_struct_ty> } + let intern_map: syn::Type = match function_type(item_fn) { + FunctionType::Constant => { + parse_quote! { salsa::interned::IdentityInterner<()> } + } + FunctionType::SalsaStruct => { + parse_quote! { salsa::interned::IdentityInterner<#salsa_struct_ty> } + } + FunctionType::RequiresInterning => { + let key_ty = key_tuple_ty(item_fn); + parse_quote! { salsa::interned::InternedIngredient } + } }; parse_quote! { @@ -325,16 +331,29 @@ fn configuration_struct(item_fn: &syn::ItemFn) -> syn::ItemStruct { } } -/// True if this fn takes more arguments. -fn requires_interning(item_fn: &syn::ItemFn) -> bool { - item_fn.sig.inputs.len() > 2 +#[derive(Debug, PartialEq, Eq, Hash)] +enum FunctionType { + Constant, + SalsaStruct, + RequiresInterning, +} + +fn function_type(item_fn: &syn::ItemFn) -> FunctionType { + match item_fn.sig.inputs.len() { + 0 => unreachable!( + "functions have been checked to have at least a database argument by this point" + ), + 1 => FunctionType::Constant, + 2 => FunctionType::SalsaStruct, + _ => FunctionType::RequiresInterning, + } } /// Every tracked fn takes a salsa struct as its second argument. /// This fn returns the type of that second argument. fn salsa_struct_ty(item_fn: &syn::ItemFn) -> syn::Type { if item_fn.sig.inputs.len() == 1 { - return parse_quote! { () }; + return parse_quote! { salsa::salsa_struct::Singleton }; } match &item_fn.sig.inputs[1] { syn::FnArg::Receiver(_) => panic!("receiver not expected"), @@ -345,10 +364,10 @@ fn salsa_struct_ty(item_fn: &syn::ItemFn) -> syn::Type { fn fn_configuration(args: &FnArgs, item_fn: &syn::ItemFn) -> Configuration { let jar_ty = args.jar_ty(); let salsa_struct_ty = salsa_struct_ty(item_fn); - let key_ty = if requires_interning(item_fn) { - parse_quote!(salsa::id::Id) - } else { - salsa_struct_ty.clone() + let key_ty = match function_type(item_fn) { + FunctionType::Constant => parse_quote!(()), + FunctionType::SalsaStruct => salsa_struct_ty.clone(), + FunctionType::RequiresInterning => parse_quote!(salsa::id::Id), }; let value_ty = configuration::value_ty(&item_fn.sig); @@ -423,29 +442,32 @@ fn ingredients_for_impl( let jar_ty = args.jar_ty(); let debug_name = crate::literal(&item_fn.sig.ident); - let intern_map: syn::Expr = if requires_interning(item_fn) { - parse_quote! { - { - let index = routes.push( - |jars| { - let jar = >::jar_from_jars(jars); - let ingredients = - <_ as salsa::storage::HasIngredientsFor>::ingredient(jar); - &ingredients.intern_map - }, - |jars| { - let jar = >::jar_from_jars_mut(jars); - let ingredients = - <_ as salsa::storage::HasIngredientsFor>::ingredient_mut(jar); - &mut ingredients.intern_map - } - ); - salsa::interned::InternedIngredient::new(index, #debug_name) + let intern_map: syn::Expr = match function_type(item_fn) { + FunctionType::Constant | FunctionType::SalsaStruct => { + parse_quote! { + salsa::interned::IdentityInterner::new() } } - } else { - parse_quote! { - salsa::interned::IdentityInterner::new() + FunctionType::RequiresInterning => { + parse_quote! { + { + let index = routes.push( + |jars| { + let jar = >::jar_from_jars(jars); + let ingredients = + <_ as salsa::storage::HasIngredientsFor>::ingredient(jar); + &ingredients.intern_map + }, + |jars| { + let jar = >::jar_from_jars_mut(jars); + let ingredients = + <_ as salsa::storage::HasIngredientsFor>::ingredient_mut(jar); + &mut ingredients.intern_map + } + ); + salsa::interned::InternedIngredient::new(index, #debug_name) + } + } } }; diff --git a/components/salsa-2022/src/salsa_struct.rs b/components/salsa-2022/src/salsa_struct.rs index 392f0134..913d85d6 100644 --- a/components/salsa-2022/src/salsa_struct.rs +++ b/components/salsa-2022/src/salsa_struct.rs @@ -4,6 +4,12 @@ pub trait SalsaStructInDb { fn register_dependent_fn(db: &DB, index: IngredientIndex); } -impl SalsaStructInDb for () { +/// A ZST that implements [`SalsaStructInDb`] +/// +/// It is used for implementing "constant" tracked function +/// (ones that only take a database as an argument). +pub struct Singleton; + +impl SalsaStructInDb for Singleton { fn register_dependent_fn(_db: &DB, _index: IngredientIndex) {} } diff --git a/salsa-2022-tests/tests/tracked_fn_constant.rs b/salsa-2022-tests/tests/tracked_fn_constant.rs index 19f5d57c..e311b138 100644 --- a/salsa-2022-tests/tests/tracked_fn_constant.rs +++ b/salsa-2022-tests/tests/tracked_fn_constant.rs @@ -7,7 +7,7 @@ struct Jar(tracked_fn); trait Db: salsa::DbWithJar {} -#[salsa::tracked(jar = Jar)] +#[salsa::tracked] fn tracked_fn(db: &dyn Db) -> u32 { 44 }