Merge branch 'salsa-rs:master' into exhaustive-macros-test

This commit is contained in:
Onigbinde Oluwamuyiwa Elijah 2022-09-07 21:39:28 +01:00 committed by GitHub
commit a2fbd1c09e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 103 additions and 39 deletions

View file

@ -11,10 +11,10 @@ pub(crate) fn tracked_fn(
mut item_fn: syn::ItemFn,
) -> syn::Result<TokenStream> {
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",
));
}
@ -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<salsa::Id, #key_ty> }
} 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<salsa::Id, #key_ty> }
}
};
parse_quote! {
@ -325,27 +331,43 @@ 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 {
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] {
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 key_ty = if requires_interning(item_fn) {
parse_quote!(salsa::id::Id)
} else {
salsa_struct_ty.clone()
let salsa_struct_ty = salsa_struct_ty(item_fn);
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);
@ -420,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 = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars(jars);
let ingredients =
<_ as salsa::storage::HasIngredientsFor<Self::Ingredients>>::ingredient(jar);
&ingredients.intern_map
},
|jars| {
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars_mut(jars);
let ingredients =
<_ as salsa::storage::HasIngredientsFor<Self::Ingredients>>::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 = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars(jars);
let ingredients =
<_ as salsa::storage::HasIngredientsFor<Self::Ingredients>>::ingredient(jar);
&ingredients.intern_map
},
|jars| {
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars_mut(jars);
let ingredients =
<_ as salsa::storage::HasIngredientsFor<Self::Ingredients>>::ingredient_mut(jar);
&mut ingredients.intern_map
}
);
salsa::interned::InternedIngredient::new(index, #debug_name)
}
}
}
};

View file

@ -3,3 +3,13 @@ use crate::{Database, IngredientIndex};
pub trait SalsaStructInDb<DB: ?Sized + Database> {
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) {}
}

View 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);
}