mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-02-08 21:35:47 +00:00
Merge #393
393: Allow "constant" tracked functions r=XFFXFF a=Skepfyr Fixes #323. This adds support for tracked functions with only a database as input, that is, it does not take a salsa struct. I'm not entirely convinced by this, it feels like it works somewhat accidentally and may be fragile to changes. I'm happy if this just get closed as I was mostly playing around to see how this worked. This change has the odd side-effect of making this code work: ```rust #[salsa::tracked] fn tracked_fn(db: &dyn Db, unit: ()) -> u32 { 44 } ``` Co-authored-by: Jack Rickard <jack.rickard@outlook.com>
This commit is contained in:
commit
4e9161e221
3 changed files with 103 additions and 39 deletions
|
@ -11,10 +11,10 @@ pub(crate) fn tracked_fn(
|
||||||
mut item_fn: syn::ItemFn,
|
mut item_fn: syn::ItemFn,
|
||||||
) -> syn::Result<TokenStream> {
|
) -> syn::Result<TokenStream> {
|
||||||
let args: FnArgs = syn::parse(args)?;
|
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(
|
return Err(syn::Error::new(
|
||||||
item_fn.sig.ident.span(),
|
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",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ pub(crate) fn tracked_fn(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(s) = &args.specify {
|
if let Some(s) = &args.specify {
|
||||||
if requires_interning(&item_fn) {
|
if function_type(&item_fn) == FunctionType::RequiresInterning {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
s.span(),
|
s.span(),
|
||||||
"tracked function takes too many arguments to have its value set with `specify`",
|
"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 visibility = &item_fn.vis;
|
||||||
|
|
||||||
let salsa_struct_ty = salsa_struct_ty(item_fn);
|
let salsa_struct_ty = salsa_struct_ty(item_fn);
|
||||||
let intern_map: syn::Type = if requires_interning(item_fn) {
|
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);
|
let key_ty = key_tuple_ty(item_fn);
|
||||||
parse_quote! { salsa::interned::InternedIngredient<salsa::Id, #key_ty> }
|
parse_quote! { salsa::interned::InternedIngredient<salsa::Id, #key_ty> }
|
||||||
} else {
|
}
|
||||||
parse_quote! { salsa::interned::IdentityInterner<#salsa_struct_ty> }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_quote! {
|
parse_quote! {
|
||||||
|
@ -325,27 +331,43 @@ fn configuration_struct(item_fn: &syn::ItemFn) -> syn::ItemStruct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// True if this fn takes more arguments.
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
fn requires_interning(item_fn: &syn::ItemFn) -> bool {
|
enum FunctionType {
|
||||||
item_fn.sig.inputs.len() > 2
|
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.
|
/// Every tracked fn takes a salsa struct as its second argument.
|
||||||
/// This fn returns the type of that 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! { salsa::salsa_struct::Singleton };
|
||||||
|
}
|
||||||
match &item_fn.sig.inputs[1] {
|
match &item_fn.sig.inputs[1] {
|
||||||
syn::FnArg::Receiver(_) => panic!("receiver not expected"),
|
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 {
|
fn fn_configuration(args: &FnArgs, item_fn: &syn::ItemFn) -> Configuration {
|
||||||
let jar_ty = args.jar_ty();
|
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) {
|
let key_ty = match function_type(item_fn) {
|
||||||
parse_quote!(salsa::id::Id)
|
FunctionType::Constant => parse_quote!(()),
|
||||||
} else {
|
FunctionType::SalsaStruct => salsa_struct_ty.clone(),
|
||||||
salsa_struct_ty.clone()
|
FunctionType::RequiresInterning => parse_quote!(salsa::id::Id),
|
||||||
};
|
};
|
||||||
let value_ty = configuration::value_ty(&item_fn.sig);
|
let value_ty = configuration::value_ty(&item_fn.sig);
|
||||||
|
|
||||||
|
@ -420,7 +442,13 @@ fn ingredients_for_impl(
|
||||||
let jar_ty = args.jar_ty();
|
let jar_ty = args.jar_ty();
|
||||||
let debug_name = crate::literal(&item_fn.sig.ident);
|
let debug_name = crate::literal(&item_fn.sig.ident);
|
||||||
|
|
||||||
let intern_map: syn::Expr = if requires_interning(item_fn) {
|
let intern_map: syn::Expr = match function_type(item_fn) {
|
||||||
|
FunctionType::Constant | FunctionType::SalsaStruct => {
|
||||||
|
parse_quote! {
|
||||||
|
salsa::interned::IdentityInterner::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FunctionType::RequiresInterning => {
|
||||||
parse_quote! {
|
parse_quote! {
|
||||||
{
|
{
|
||||||
let index = routes.push(
|
let index = routes.push(
|
||||||
|
@ -440,9 +468,6 @@ fn ingredients_for_impl(
|
||||||
salsa::interned::InternedIngredient::new(index, #debug_name)
|
salsa::interned::InternedIngredient::new(index, #debug_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
parse_quote! {
|
|
||||||
salsa::interned::IdentityInterner::new()
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,3 +3,13 @@ use crate::{Database, IngredientIndex};
|
||||||
pub trait SalsaStructInDb<DB: ?Sized + Database> {
|
pub trait SalsaStructInDb<DB: ?Sized + Database> {
|
||||||
fn register_dependent_fn(db: &DB, index: IngredientIndex);
|
fn register_dependent_fn(db: &DB, index: IngredientIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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<DB: ?Sized + Database> SalsaStructInDb<DB> for Singleton {
|
||||||
|
fn register_dependent_fn(_db: &DB, _index: IngredientIndex) {}
|
||||||
|
}
|
||||||
|
|
29
salsa-2022-tests/tests/tracked_fn_constant.rs
Normal file
29
salsa-2022-tests/tests/tracked_fn_constant.rs
Normal file
|
@ -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<Jar> {}
|
||||||
|
|
||||||
|
#[salsa::tracked]
|
||||||
|
fn tracked_fn(db: &dyn Db) -> u32 {
|
||||||
|
44
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn execute() {
|
||||||
|
#[salsa::db(Jar)]
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Database {
|
||||||
|
storage: salsa::Storage<Self>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl salsa::Database for Database {}
|
||||||
|
|
||||||
|
impl Db for Database {}
|
||||||
|
|
||||||
|
let mut db = Database::default();
|
||||||
|
assert_eq!(tracked_fn(&db), 44);
|
||||||
|
}
|
Loading…
Reference in a new issue