mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-01-22 21:05:11 +00:00
Merge branch 'salsa-rs:master' into singleton
This commit is contained in:
commit
fac05b3c9f
31 changed files with 1376 additions and 137 deletions
|
@ -41,6 +41,7 @@ To learn more about Salsa, try one of the following:
|
|||
|
||||
- read the [heavily commented `hello_world` example](https://github.com/salsa-rs/salsa/blob/master/examples/hello_world/main.rs);
|
||||
- check out the [Salsa book](https://salsa-rs.github.io/salsa);
|
||||
- [中文版](https://zjp-cn.github.io/salsa)
|
||||
- watch one of our [videos](https://salsa-rs.github.io/salsa/videos.html).
|
||||
|
||||
## Getting in touch
|
||||
|
|
|
@ -10,5 +10,5 @@ proc-macro = true
|
|||
heck = "0.4"
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = { version = "1.0", features = ["full", "extra-traits"] }
|
||||
syn = { version = "1.0", features = ["full", "extra-traits", "visit-mut"] }
|
||||
eyre = "0.6.5"
|
||||
|
|
|
@ -51,8 +51,6 @@ impl InputStruct {
|
|||
fn generate_input(&self) -> syn::Result<TokenStream> {
|
||||
self.validate_input()?;
|
||||
|
||||
let (config_structs, config_impls) = self.field_config_structs_and_impls(self.all_fields());
|
||||
|
||||
let id_struct = self.id_struct();
|
||||
let inherent_impl = self.input_inherent_impl();
|
||||
let ingredients_for_impl = self.input_ingredients();
|
||||
|
@ -61,13 +59,11 @@ impl InputStruct {
|
|||
let as_debug_with_db_impl = self.as_debug_with_db_impl();
|
||||
|
||||
Ok(quote! {
|
||||
#(#config_structs)*
|
||||
#id_struct
|
||||
#inherent_impl
|
||||
#ingredients_for_impl
|
||||
#as_id_impl
|
||||
#as_debug_with_db_impl
|
||||
#(#config_impls)*
|
||||
#salsa_struct_in_db_impl
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,14 +5,17 @@ pub(crate) fn tracked(
|
|||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let item = syn::parse_macro_input!(input as Item);
|
||||
match item {
|
||||
let res = match item {
|
||||
syn::Item::Struct(item) => crate::tracked_struct::tracked(args, item),
|
||||
syn::Item::Fn(item) => crate::tracked_fn::tracked(args, item),
|
||||
_ => syn::Error::new(
|
||||
syn::Item::Fn(item) => crate::tracked_fn::tracked_fn(args, item),
|
||||
syn::Item::Impl(item) => crate::tracked_fn::tracked_impl(args, item),
|
||||
_ => Err(syn::Error::new(
|
||||
item.span(),
|
||||
&"tracked can be applied to structs and functions only".to_string(),
|
||||
)
|
||||
.into_compile_error()
|
||||
.into(),
|
||||
"tracked can only be applied to structs, functions, and impls",
|
||||
)),
|
||||
};
|
||||
match res {
|
||||
Ok(s) => s.into(),
|
||||
Err(err) => err.into_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,31 +1,32 @@
|
|||
use proc_macro2::{Literal, TokenStream};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::visit_mut::VisitMut;
|
||||
use syn::{ReturnType, Token};
|
||||
|
||||
use crate::configuration::{self, Configuration, CycleRecoveryStrategy};
|
||||
use crate::options::Options;
|
||||
|
||||
pub(crate) fn tracked(
|
||||
pub(crate) fn tracked_fn(
|
||||
args: proc_macro::TokenStream,
|
||||
item_fn: syn::ItemFn,
|
||||
) -> proc_macro::TokenStream {
|
||||
let args = syn::parse_macro_input!(args as Args);
|
||||
match tracked_fn(args, item_fn) {
|
||||
Ok(p) => p.into(),
|
||||
Err(e) => e.into_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn tracked_fn(args: Args, item_fn: syn::ItemFn) -> syn::Result<TokenStream> {
|
||||
if item_fn.sig.inputs.len() <= 1 {
|
||||
mut item_fn: syn::ItemFn,
|
||||
) -> syn::Result<TokenStream> {
|
||||
let args: FnArgs = syn::parse(args)?;
|
||||
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",
|
||||
));
|
||||
}
|
||||
|
||||
if let syn::FnArg::Receiver(receiver) = &item_fn.sig.inputs[0] {
|
||||
return Err(syn::Error::new(
|
||||
receiver.span(),
|
||||
"#[salsa::tracked] must also be applied to the impl block for tracked methods",
|
||||
));
|
||||
}
|
||||
|
||||
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`",
|
||||
|
@ -40,28 +41,20 @@ fn tracked_fn(args: Args, item_fn: syn::ItemFn) -> syn::Result<TokenStream> {
|
|||
}
|
||||
}
|
||||
|
||||
let struct_item = configuration_struct(&item_fn);
|
||||
let configuration = fn_configuration(&args, &item_fn);
|
||||
let struct_item_ident = &struct_item.ident;
|
||||
let config_ty: syn::Type = parse_quote!(#struct_item_ident);
|
||||
let configuration_impl = configuration.to_impl(&config_ty);
|
||||
let ingredients_for_impl = ingredients_for_impl(&args, &item_fn, &config_ty);
|
||||
let (getter, item_impl) = wrapper_fns(&args, &item_fn, &config_ty)?;
|
||||
let (config_ty, fn_struct) = fn_struct(&args, &item_fn)?;
|
||||
*item_fn.block = getter_fn(&args, &mut item_fn.sig, item_fn.block.span(), &config_ty)?;
|
||||
|
||||
Ok(quote! {
|
||||
#struct_item
|
||||
#configuration_impl
|
||||
#ingredients_for_impl
|
||||
#fn_struct
|
||||
|
||||
// we generate a `'db` lifetime that clippy
|
||||
// sometimes doesn't like
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
#getter
|
||||
#item_impl
|
||||
#item_fn
|
||||
})
|
||||
}
|
||||
|
||||
type Args = Options<TrackedFn>;
|
||||
type FnArgs = Options<TrackedFn>;
|
||||
|
||||
struct TrackedFn;
|
||||
|
||||
|
@ -87,6 +80,219 @@ impl crate::options::AllowedOptions for TrackedFn {
|
|||
const CONSTRUCTOR_NAME: bool = false;
|
||||
}
|
||||
|
||||
type ImplArgs = Options<TrackedImpl>;
|
||||
|
||||
pub(crate) fn tracked_impl(
|
||||
args: proc_macro::TokenStream,
|
||||
mut item_impl: syn::ItemImpl,
|
||||
) -> syn::Result<TokenStream> {
|
||||
let args: ImplArgs = syn::parse(args)?;
|
||||
let self_type = match &*item_impl.self_ty {
|
||||
syn::Type::Path(path) => path,
|
||||
_ => {
|
||||
return Err(syn::Error::new(
|
||||
item_impl.self_ty.span(),
|
||||
"#[salsa::tracked] can only be applied to salsa structs",
|
||||
))
|
||||
}
|
||||
};
|
||||
let self_type_name = &self_type.path.segments.last().unwrap().ident;
|
||||
let name_prefix = match &item_impl.trait_ {
|
||||
Some((_, trait_name, _)) => format!(
|
||||
"{}_{}",
|
||||
self_type_name,
|
||||
trait_name.segments.last().unwrap().ident
|
||||
),
|
||||
None => format!("{}", self_type_name),
|
||||
};
|
||||
let extra_impls = item_impl
|
||||
.items
|
||||
.iter_mut()
|
||||
.filter_map(|item| {
|
||||
let item_method = match item {
|
||||
syn::ImplItem::Method(item_method) => item_method,
|
||||
_ => return None,
|
||||
};
|
||||
let salsa_tracked_attr = item_method.attrs.iter().position(|attr| {
|
||||
let path = &attr.path.segments;
|
||||
path.len() == 2
|
||||
&& path[0].arguments == syn::PathArguments::None
|
||||
&& path[0].ident == "salsa"
|
||||
&& path[1].arguments == syn::PathArguments::None
|
||||
&& path[1].ident == "tracked"
|
||||
})?;
|
||||
let salsa_tracked_attr = item_method.attrs.remove(salsa_tracked_attr);
|
||||
let inner_args = if !salsa_tracked_attr.tokens.is_empty() {
|
||||
salsa_tracked_attr.parse_args()
|
||||
} else {
|
||||
Ok(FnArgs::default())
|
||||
};
|
||||
let inner_args = match inner_args {
|
||||
Ok(inner_args) => inner_args,
|
||||
Err(err) => return Some(Err(err)),
|
||||
};
|
||||
let name = format!("{}_{}", name_prefix, item_method.sig.ident);
|
||||
Some(tracked_method(
|
||||
&args,
|
||||
inner_args,
|
||||
item_method,
|
||||
self_type,
|
||||
&name,
|
||||
))
|
||||
})
|
||||
// Collate all the errors so we can display them all at once
|
||||
.fold(Ok(Vec::new()), |mut acc, res| {
|
||||
match (&mut acc, res) {
|
||||
(Ok(extra_impls), Ok(impls)) => extra_impls.push(impls),
|
||||
(Ok(_), Err(err)) => acc = Err(err),
|
||||
(Err(_), Ok(_)) => {}
|
||||
(Err(errors), Err(err)) => errors.combine(err),
|
||||
}
|
||||
acc
|
||||
})?;
|
||||
|
||||
Ok(quote! {
|
||||
#item_impl
|
||||
|
||||
#(#extra_impls)*
|
||||
})
|
||||
}
|
||||
|
||||
struct TrackedImpl;
|
||||
|
||||
impl crate::options::AllowedOptions for TrackedImpl {
|
||||
const RETURN_REF: bool = false;
|
||||
|
||||
const SPECIFY: bool = false;
|
||||
|
||||
const NO_EQ: bool = false;
|
||||
|
||||
const JAR: bool = true;
|
||||
|
||||
const DATA: bool = false;
|
||||
|
||||
const DB: bool = false;
|
||||
|
||||
const RECOVERY_FN: bool = false;
|
||||
|
||||
const LRU: bool = false;
|
||||
|
||||
const CONSTRUCTOR_NAME: bool = false;
|
||||
}
|
||||
|
||||
fn tracked_method(
|
||||
outer_args: &ImplArgs,
|
||||
mut args: FnArgs,
|
||||
item_method: &mut syn::ImplItemMethod,
|
||||
self_type: &syn::TypePath,
|
||||
name: &str,
|
||||
) -> syn::Result<TokenStream> {
|
||||
args.jar_ty = args.jar_ty.or_else(|| outer_args.jar_ty.clone());
|
||||
|
||||
if item_method.sig.inputs.len() <= 1 {
|
||||
return Err(syn::Error::new(
|
||||
item_method.sig.ident.span(),
|
||||
"tracked methods must have at least self and a database argument",
|
||||
));
|
||||
}
|
||||
|
||||
let mut item_fn = syn::ItemFn {
|
||||
attrs: item_method.attrs.clone(),
|
||||
vis: item_method.vis.clone(),
|
||||
sig: item_method.sig.clone(),
|
||||
block: Box::new(rename_self_in_block(item_method.block.clone())?),
|
||||
};
|
||||
item_fn.sig.ident = syn::Ident::new(name, item_fn.sig.ident.span());
|
||||
// Flip the first and second arguments as the rest of the code expects the
|
||||
// database to come first and the struct to come second. We also need to
|
||||
// change the self argument to a normal typed argument called __salsa_self.
|
||||
let mut original_inputs = item_fn.sig.inputs.into_pairs();
|
||||
let self_param = match original_inputs.next().unwrap().into_value() {
|
||||
syn::FnArg::Receiver(r) if r.reference.is_none() => r,
|
||||
arg => return Err(syn::Error::new(arg.span(), "first argument must be self")),
|
||||
};
|
||||
let db_param = original_inputs.next().unwrap().into_value();
|
||||
let mut inputs = syn::punctuated::Punctuated::new();
|
||||
inputs.push(db_param);
|
||||
inputs.push(syn::FnArg::Typed(syn::PatType {
|
||||
attrs: self_param.attrs,
|
||||
pat: Box::new(syn::Pat::Ident(syn::PatIdent {
|
||||
attrs: Vec::new(),
|
||||
by_ref: None,
|
||||
mutability: self_param.mutability,
|
||||
ident: syn::Ident::new("__salsa_self", self_param.self_token.span),
|
||||
subpat: None,
|
||||
})),
|
||||
colon_token: Default::default(),
|
||||
ty: Box::new(syn::Type::Path(self_type.clone())),
|
||||
}));
|
||||
inputs.push_punct(Default::default());
|
||||
inputs.extend(original_inputs);
|
||||
item_fn.sig.inputs = inputs;
|
||||
|
||||
let (config_ty, fn_struct) = crate::tracked_fn::fn_struct(&args, &item_fn)?;
|
||||
|
||||
item_method.block = getter_fn(
|
||||
&args,
|
||||
&mut item_method.sig,
|
||||
item_method.block.span(),
|
||||
&config_ty,
|
||||
)?;
|
||||
|
||||
Ok(fn_struct)
|
||||
}
|
||||
|
||||
/// Rename all occurrences of `self` to `__salsa_self` in a block
|
||||
/// so that it can be used in a free function.
|
||||
fn rename_self_in_block(mut block: syn::Block) -> syn::Result<syn::Block> {
|
||||
struct RenameIdent(syn::Result<()>);
|
||||
|
||||
impl syn::visit_mut::VisitMut for RenameIdent {
|
||||
fn visit_ident_mut(&mut self, i: &mut syn::Ident) {
|
||||
if i == "__salsa_self" {
|
||||
let err = syn::Error::new(
|
||||
i.span(),
|
||||
"Existing variable name clashes with 'self' -> '__salsa_self' renaming",
|
||||
);
|
||||
match &mut self.0 {
|
||||
Ok(()) => self.0 = Err(err),
|
||||
Err(errors) => errors.combine(err),
|
||||
}
|
||||
}
|
||||
if i == "self" {
|
||||
*i = syn::Ident::new("__salsa_self", i.span());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut rename = RenameIdent(Ok(()));
|
||||
rename.visit_block_mut(&mut block);
|
||||
rename.0.map(move |()| block)
|
||||
}
|
||||
|
||||
/// Create the struct representing the function and all of its impls.
|
||||
///
|
||||
/// This returns the name of the constructed type and the code defining everything.
|
||||
fn fn_struct(args: &FnArgs, item_fn: &syn::ItemFn) -> syn::Result<(syn::Type, TokenStream)> {
|
||||
let struct_item = configuration_struct(item_fn);
|
||||
let configuration = fn_configuration(args, item_fn);
|
||||
let struct_item_ident = &struct_item.ident;
|
||||
let config_ty: syn::Type = parse_quote!(#struct_item_ident);
|
||||
let configuration_impl = configuration.to_impl(&config_ty);
|
||||
let ingredients_for_impl = ingredients_for_impl(args, item_fn, &config_ty);
|
||||
let item_impl = setter_impl(args, item_fn, &config_ty)?;
|
||||
|
||||
Ok((
|
||||
config_ty,
|
||||
quote! {
|
||||
#struct_item
|
||||
#configuration_impl
|
||||
#ingredients_for_impl
|
||||
#item_impl
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns the key type for this tracked function.
|
||||
/// This is a tuple of all the argument types (apart from the database).
|
||||
fn key_tuple_ty(item_fn: &syn::ItemFn) -> syn::Type {
|
||||
|
@ -105,11 +311,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! {
|
||||
|
@ -121,27 +333,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: &Args, item_fn: &syn::ItemFn) -> Configuration {
|
||||
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);
|
||||
|
||||
|
@ -209,36 +437,39 @@ fn fn_configuration(args: &Args, item_fn: &syn::ItemFn) -> Configuration {
|
|||
}
|
||||
|
||||
fn ingredients_for_impl(
|
||||
args: &Args,
|
||||
args: &FnArgs,
|
||||
item_fn: &syn::ItemFn,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::ItemImpl {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -284,14 +515,11 @@ fn ingredients_for_impl(
|
|||
}
|
||||
}
|
||||
|
||||
fn wrapper_fns(
|
||||
args: &Args,
|
||||
fn setter_impl(
|
||||
args: &FnArgs,
|
||||
item_fn: &syn::ItemFn,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::Result<(syn::ItemFn, syn::ItemImpl)> {
|
||||
// The "getter" has same signature as the original:
|
||||
let getter_fn = getter_fn(args, item_fn, config_ty)?;
|
||||
|
||||
) -> syn::Result<syn::ItemImpl> {
|
||||
let ref_getter_fn = ref_getter_fn(args, item_fn, config_ty)?;
|
||||
let accumulated_fn = accumulated_fn(args, item_fn, config_ty)?;
|
||||
let setter_fn = setter_fn(args, item_fn, config_ty)?;
|
||||
|
@ -315,45 +543,53 @@ fn wrapper_fns(
|
|||
}
|
||||
};
|
||||
|
||||
Ok((getter_fn, setter_impl))
|
||||
Ok(setter_impl)
|
||||
}
|
||||
|
||||
/// Creates the `get` associated function.
|
||||
/// Creates the shim function that looks like the original function but calls
|
||||
/// into the machinery we've just generated rather than executing the code.
|
||||
fn getter_fn(
|
||||
args: &Args,
|
||||
item_fn: &syn::ItemFn,
|
||||
args: &FnArgs,
|
||||
fn_sig: &mut syn::Signature,
|
||||
block_span: proc_macro2::Span,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::Result<syn::ItemFn> {
|
||||
let mut getter_fn = item_fn.clone();
|
||||
let arg_idents: Vec<_> = item_fn
|
||||
.sig
|
||||
) -> syn::Result<syn::Block> {
|
||||
let mut is_method = false;
|
||||
let mut arg_idents: Vec<_> = fn_sig
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|arg| -> syn::Result<syn::Ident> {
|
||||
match arg {
|
||||
syn::FnArg::Receiver(_) => Err(syn::Error::new(arg.span(), "unexpected receiver")),
|
||||
syn::FnArg::Receiver(receiver) => {
|
||||
is_method = true;
|
||||
Ok(syn::Ident::new("self", receiver.self_token.span()))
|
||||
}
|
||||
syn::FnArg::Typed(pat_ty) => Ok(match &*pat_ty.pat {
|
||||
syn::Pat::Ident(ident) => ident.ident.clone(),
|
||||
_ => return Err(syn::Error::new(arg.span(), "unexpected receiver")),
|
||||
_ => return Err(syn::Error::new(arg.span(), "unsupported argument kind")),
|
||||
}),
|
||||
}
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
if args.return_ref.is_some() {
|
||||
getter_fn = make_fn_return_ref(getter_fn)?;
|
||||
getter_fn.block = Box::new(parse_quote_spanned! {
|
||||
item_fn.block.span() => {
|
||||
// If this is a method then the order of the database and the salsa struct are reversed
|
||||
// because the self argument must always come first.
|
||||
if is_method {
|
||||
arg_idents.swap(0, 1);
|
||||
}
|
||||
Ok(if args.return_ref.is_some() {
|
||||
make_fn_return_ref(fn_sig)?;
|
||||
parse_quote_spanned! {
|
||||
block_span => {
|
||||
#config_ty::get(#(#arg_idents,)*)
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
getter_fn.block = Box::new(parse_quote_spanned! {
|
||||
item_fn.block.span() => {
|
||||
parse_quote_spanned! {
|
||||
block_span => {
|
||||
Clone::clone(#config_ty::get(#(#arg_idents,)*))
|
||||
}
|
||||
});
|
||||
}
|
||||
Ok(getter_fn)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a `get` associated function that returns `&Value`
|
||||
|
@ -361,14 +597,14 @@ fn getter_fn(
|
|||
///
|
||||
/// (Helper for `getter_fn`)
|
||||
fn ref_getter_fn(
|
||||
args: &Args,
|
||||
args: &FnArgs,
|
||||
item_fn: &syn::ItemFn,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::Result<syn::ItemFn> {
|
||||
let jar_ty = args.jar_ty();
|
||||
let mut ref_getter_fn = item_fn.clone();
|
||||
ref_getter_fn.sig.ident = syn::Ident::new("get", item_fn.sig.ident.span());
|
||||
ref_getter_fn = make_fn_return_ref(ref_getter_fn)?;
|
||||
make_fn_return_ref(&mut ref_getter_fn.sig)?;
|
||||
|
||||
let (db_var, arg_names) = fn_args(item_fn)?;
|
||||
ref_getter_fn.block = parse_quote! {
|
||||
|
@ -386,7 +622,7 @@ fn ref_getter_fn(
|
|||
/// Creates a `set` associated function that can be used to set (given an `&mut db`)
|
||||
/// the value for this function for some inputs.
|
||||
fn setter_fn(
|
||||
args: &Args,
|
||||
args: &FnArgs,
|
||||
item_fn: &syn::ItemFn,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::Result<syn::ImplItemMethod> {
|
||||
|
@ -439,7 +675,7 @@ fn setter_fn(
|
|||
/// my_tracked_fn::set_lru_capacity(16)
|
||||
/// ```
|
||||
fn set_lru_capacity_fn(
|
||||
args: &Args,
|
||||
args: &FnArgs,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::Result<Option<syn::ImplItemMethod>> {
|
||||
if args.lru.is_none() {
|
||||
|
@ -460,7 +696,7 @@ fn set_lru_capacity_fn(
|
|||
}
|
||||
|
||||
fn specify_fn(
|
||||
args: &Args,
|
||||
args: &FnArgs,
|
||||
item_fn: &syn::ItemFn,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::Result<Option<syn::ImplItemMethod>> {
|
||||
|
@ -493,19 +729,16 @@ fn specify_fn(
|
|||
},
|
||||
}))
|
||||
}
|
||||
/// Given a function def tagged with `#[return_ref]`, modifies `ref_getter_fn`
|
||||
/// so that it returns an `&Value` instead of `Value`. May introduce a name for the
|
||||
/// Given a function def tagged with `#[return_ref]`, modifies `fn_sig` so that
|
||||
/// it returns an `&Value` instead of `Value`. May introduce a name for the
|
||||
/// database lifetime if required.
|
||||
fn make_fn_return_ref(mut ref_getter_fn: syn::ItemFn) -> syn::Result<syn::ItemFn> {
|
||||
// The 0th input should be a `&dyn Foo`. We need to ensure
|
||||
// it has a named lifetime parameter.
|
||||
let (db_lifetime, _) = db_lifetime_and_ty(&mut ref_getter_fn)?;
|
||||
fn make_fn_return_ref(mut fn_sig: &mut syn::Signature) -> syn::Result<()> {
|
||||
// An input should be a `&dyn Db`.
|
||||
// We need to ensure it has a named lifetime parameter.
|
||||
let (db_lifetime, _) = db_lifetime_and_ty(fn_sig)?;
|
||||
|
||||
let (right_arrow, elem) = match ref_getter_fn.sig.output {
|
||||
ReturnType::Default => (
|
||||
syn::Token![->](ref_getter_fn.sig.paren_token.span),
|
||||
parse_quote!(()),
|
||||
),
|
||||
let (right_arrow, elem) = match fn_sig.output.clone() {
|
||||
ReturnType::Default => (syn::Token![->](fn_sig.paren_token.span), parse_quote!(())),
|
||||
ReturnType::Type(rarrow, ty) => (rarrow, ty),
|
||||
};
|
||||
|
||||
|
@ -516,24 +749,31 @@ fn make_fn_return_ref(mut ref_getter_fn: syn::ItemFn) -> syn::Result<syn::ItemFn
|
|||
elem,
|
||||
};
|
||||
|
||||
ref_getter_fn.sig.output = syn::ReturnType::Type(right_arrow, Box::new(ref_output.into()));
|
||||
fn_sig.output = syn::ReturnType::Type(right_arrow, Box::new(ref_output.into()));
|
||||
|
||||
Ok(ref_getter_fn)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Given an item function, identifies the name given to the `&dyn Db` reference and returns it,
|
||||
/// along with the type of the database. If the database lifetime did not have a name,
|
||||
/// then modifies the item function so that it is called `'__db` and returns that.
|
||||
fn db_lifetime_and_ty(func: &mut syn::ItemFn) -> syn::Result<(syn::Lifetime, &syn::Type)> {
|
||||
match &mut func.sig.inputs[0] {
|
||||
syn::FnArg::Receiver(r) => Err(syn::Error::new(r.span(), "expected database, not self")),
|
||||
/// Given a function signature, identifies the name given to the `&dyn Db` reference
|
||||
/// and returns it, along with the type of the database.
|
||||
/// If the database lifetime did not have a name, then modifies the item function
|
||||
/// so that it is called `'__db` and returns that.
|
||||
fn db_lifetime_and_ty(func: &mut syn::Signature) -> syn::Result<(syn::Lifetime, &syn::Type)> {
|
||||
// If this is a method, then the database should be the second argument.
|
||||
let db_loc = if matches!(func.inputs[0], syn::FnArg::Receiver(_)) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
match &mut func.inputs[db_loc] {
|
||||
syn::FnArg::Receiver(r) => Err(syn::Error::new(r.span(), "two self arguments")),
|
||||
syn::FnArg::Typed(pat_ty) => match &mut *pat_ty.ty {
|
||||
syn::Type::Reference(ty) => match &ty.lifetime {
|
||||
Some(lt) => Ok((lt.clone(), &pat_ty.ty)),
|
||||
None => {
|
||||
let and_token_span = ty.and_token.span();
|
||||
let ident = syn::Ident::new("__db", and_token_span);
|
||||
func.sig.generics.params.insert(
|
||||
func.generics.params.insert(
|
||||
0,
|
||||
syn::LifetimeDef {
|
||||
attrs: vec![],
|
||||
|
@ -566,7 +806,7 @@ fn db_lifetime_and_ty(func: &mut syn::ItemFn) -> syn::Result<(syn::Lifetime, &sy
|
|||
/// on the function ingredient to extract the values pushed (transitively)
|
||||
/// into an accumulator.
|
||||
fn accumulated_fn(
|
||||
args: &Args,
|
||||
args: &FnArgs,
|
||||
item_fn: &syn::ItemFn,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::Result<syn::ItemFn> {
|
||||
|
@ -581,7 +821,7 @@ fn accumulated_fn(
|
|||
-> Vec<<__A as salsa::accumulator::Accumulator>::Data>
|
||||
};
|
||||
|
||||
let (db_lifetime, _) = db_lifetime_and_ty(&mut accumulated_fn)?;
|
||||
let (db_lifetime, _) = db_lifetime_and_ty(&mut accumulated_fn.sig)?;
|
||||
let predicate: syn::WherePredicate = parse_quote!(<#jar_ty as salsa::jar::Jar<#db_lifetime>>::DynDb: salsa::storage::HasJar<<__A as salsa::accumulator::Accumulator>::Jar>);
|
||||
|
||||
if let Some(where_clause) = &mut accumulated_fn.sig.generics.where_clause {
|
||||
|
|
|
@ -10,13 +10,8 @@ use crate::salsa_struct::{SalsaField, SalsaStruct};
|
|||
pub(crate) fn tracked(
|
||||
args: proc_macro::TokenStream,
|
||||
struct_item: syn::ItemStruct,
|
||||
) -> proc_macro::TokenStream {
|
||||
match SalsaStruct::with_struct(args, struct_item)
|
||||
.and_then(|el| TrackedStruct(el).generate_tracked())
|
||||
{
|
||||
Ok(s) => s.into(),
|
||||
Err(err) => err.into_compile_error().into(),
|
||||
}
|
||||
) -> syn::Result<TokenStream> {
|
||||
SalsaStruct::with_struct(args, struct_item).and_then(|el| TrackedStruct(el).generate_tracked())
|
||||
}
|
||||
|
||||
struct TrackedStruct(SalsaStruct<Self>);
|
||||
|
|
|
@ -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) {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
#[salsa::jar(db = Db)]
|
||||
struct Jar(AccTwoUnnamedFields, AccNamedField);
|
||||
|
||||
trait Db: salsa::DbWithJar<Jar> {}
|
||||
|
||||
// accumulator with more than one unnamed fields
|
||||
#[salsa::accumulator(jar = Jar)]
|
||||
struct AccTwoUnnamedFields (u32, u32);
|
||||
|
||||
|
||||
// accumulator with named fields
|
||||
#[salsa::accumulator(jar = Jar)]
|
||||
struct AccNamedField {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,23 @@
|
|||
error: accumulator structs should have only one anonymous field
|
||||
--> tests/compile-fail/accumulator_fields_incompatibles.rs:8:8
|
||||
|
|
||||
8 | struct AccTwoUnnamedFields (u32, u32);
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: accumulator structs should have only one anonymous field
|
||||
--> tests/compile-fail/accumulator_fields_incompatibles.rs:13:8
|
||||
|
|
||||
13 | struct AccNamedField {
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error[E0412]: cannot find type `AccTwoUnnamedFields` in this scope
|
||||
--> tests/compile-fail/accumulator_fields_incompatibles.rs:2:12
|
||||
|
|
||||
2 | struct Jar(AccTwoUnnamedFields, AccNamedField);
|
||||
| ^^^^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `AccNamedField` in this scope
|
||||
--> tests/compile-fail/accumulator_fields_incompatibles.rs:2:33
|
||||
|
|
||||
2 | struct Jar(AccTwoUnnamedFields, AccNamedField);
|
||||
| ^^^^^^^^^^^^^ not found in this scope
|
|
@ -0,0 +1,30 @@
|
|||
#[salsa::jar(db = Db)]
|
||||
struct Jar(AccWithRetRef, AccWithSpecify, AccWithNoEq, AccWithData, AcWithcDb, AccWithRecover, AccWithLru, AccWithConstructor);
|
||||
|
||||
trait Db: salsa::DbWithJar<Jar> {}
|
||||
|
||||
#[salsa::accumulator(jar = Jar, return_ref)]
|
||||
struct AccWithRetRef (u32);
|
||||
|
||||
#[salsa::accumulator(jar = Jar, specify)]
|
||||
struct AccWithSpecify (u32);
|
||||
|
||||
#[salsa::accumulator(jar = Jar, no_eq)]
|
||||
struct AccWithNoEq (u32);
|
||||
|
||||
#[salsa::accumulator(jar = Jar, data = MyAcc)]
|
||||
struct AccWithData (u32);
|
||||
|
||||
#[salsa::accumulator(jar = Jar, db = Db)]
|
||||
struct AcWithcDb (u32);
|
||||
|
||||
#[salsa::accumulator(jar = Jar, recover_fn = recover)]
|
||||
struct AccWithRecover (u32);
|
||||
|
||||
#[salsa::accumulator(jar = Jar, lru =12)]
|
||||
struct AccWithLru (u32);
|
||||
|
||||
#[salsa::accumulator(jar = Jar, constructor = Constructor)]
|
||||
struct AccWithConstructor (u32);
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,95 @@
|
|||
error: `return_ref` option not allowed here
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:6:33
|
||||
|
|
||||
6 | #[salsa::accumulator(jar = Jar, return_ref)]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `specify` option not allowed here
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:9:33
|
||||
|
|
||||
9 | #[salsa::accumulator(jar = Jar, specify)]
|
||||
| ^^^^^^^
|
||||
|
||||
error: `no_eq` option not allowed here
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:12:33
|
||||
|
|
||||
12 | #[salsa::accumulator(jar = Jar, no_eq)]
|
||||
| ^^^^^
|
||||
|
||||
error: `data` option not allowed here
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:15:33
|
||||
|
|
||||
15 | #[salsa::accumulator(jar = Jar, data = MyAcc)]
|
||||
| ^^^^
|
||||
|
||||
error: `db` option not allowed here
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:18:33
|
||||
|
|
||||
18 | #[salsa::accumulator(jar = Jar, db = Db)]
|
||||
| ^^
|
||||
|
||||
error: unrecognized option `recover_fn`
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:21:33
|
||||
|
|
||||
21 | #[salsa::accumulator(jar = Jar, recover_fn = recover)]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `lru` option not allowed here
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:24:33
|
||||
|
|
||||
24 | #[salsa::accumulator(jar = Jar, lru =12)]
|
||||
| ^^^
|
||||
|
||||
error: `constructor` option not allowed here
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:27:33
|
||||
|
|
||||
27 | #[salsa::accumulator(jar = Jar, constructor = Constructor)]
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error[E0412]: cannot find type `AccWithRetRef` in this scope
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:2:12
|
||||
|
|
||||
2 | struct Jar(AccWithRetRef, AccWithSpecify, AccWithNoEq, AccWithData, AcWithcDb, AccWithRecover, AccWithLru, AccWithConstructor);
|
||||
| ^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `AccWithSpecify` in this scope
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:2:27
|
||||
|
|
||||
2 | struct Jar(AccWithRetRef, AccWithSpecify, AccWithNoEq, AccWithData, AcWithcDb, AccWithRecover, AccWithLru, AccWithConstructor);
|
||||
| ^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `AccWithNoEq` in this scope
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:2:43
|
||||
|
|
||||
2 | struct Jar(AccWithRetRef, AccWithSpecify, AccWithNoEq, AccWithData, AcWithcDb, AccWithRecover, AccWithLru, AccWithConstructor);
|
||||
| ^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `AccWithData` in this scope
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:2:56
|
||||
|
|
||||
2 | struct Jar(AccWithRetRef, AccWithSpecify, AccWithNoEq, AccWithData, AcWithcDb, AccWithRecover, AccWithLru, AccWithConstructor);
|
||||
| ^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `AcWithcDb` in this scope
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:2:69
|
||||
|
|
||||
2 | struct Jar(AccWithRetRef, AccWithSpecify, AccWithNoEq, AccWithData, AcWithcDb, AccWithRecover, AccWithLru, AccWithConstructor);
|
||||
| ^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `AccWithRecover` in this scope
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:2:80
|
||||
|
|
||||
2 | struct Jar(AccWithRetRef, AccWithSpecify, AccWithNoEq, AccWithData, AcWithcDb, AccWithRecover, AccWithLru, AccWithConstructor);
|
||||
| ^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `AccWithLru` in this scope
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:2:96
|
||||
|
|
||||
2 | struct Jar(AccWithRetRef, AccWithSpecify, AccWithNoEq, AccWithData, AcWithcDb, AccWithRecover, AccWithLru, AccWithConstructor);
|
||||
| ^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `AccWithConstructor` in this scope
|
||||
--> tests/compile-fail/accumulator_incompatibles.rs:2:108
|
||||
|
|
||||
2 | struct Jar(AccWithRetRef, AccWithSpecify, AccWithNoEq, AccWithData, AcWithcDb, AccWithRecover, AccWithLru, AccWithConstructor);
|
||||
| ^^^^^^^^^^^^^^^^^^ not found in this scope
|
|
@ -0,0 +1,24 @@
|
|||
#[salsa::jar(db = Db)]
|
||||
struct Jar(InputWithRetRef, InputWithSpecify, InputNoWithEq, InputWithDb, InputWithRecover, InputWithLru);
|
||||
|
||||
trait Db: salsa::DbWithJar<Jar> {}
|
||||
|
||||
#[salsa::input(jar = Jar, return_ref)]
|
||||
struct InputWithRetRef (u32);
|
||||
|
||||
#[salsa::input(jar = Jar, specify)]
|
||||
struct InputWithSpecify (u32);
|
||||
|
||||
#[salsa::input(jar = Jar, no_eq)]
|
||||
struct InputNoWithEq (u32);
|
||||
|
||||
#[salsa::input(jar = Jar, db = Db)]
|
||||
struct InputWithDb (u32);
|
||||
|
||||
#[salsa::input(jar = Jar, recover_fn = recover)]
|
||||
struct InputWithRecover (u32);
|
||||
|
||||
#[salsa::input(jar = Jar, lru =12)]
|
||||
struct InputWithLru (u32);
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,71 @@
|
|||
error: `return_ref` option not allowed here
|
||||
--> tests/compile-fail/input_struct_incompatibles.rs:6:27
|
||||
|
|
||||
6 | #[salsa::input(jar = Jar, return_ref)]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `specify` option not allowed here
|
||||
--> tests/compile-fail/input_struct_incompatibles.rs:9:27
|
||||
|
|
||||
9 | #[salsa::input(jar = Jar, specify)]
|
||||
| ^^^^^^^
|
||||
|
||||
error: `no_eq` option not allowed here
|
||||
--> tests/compile-fail/input_struct_incompatibles.rs:12:27
|
||||
|
|
||||
12 | #[salsa::input(jar = Jar, no_eq)]
|
||||
| ^^^^^
|
||||
|
||||
error: `db` option not allowed here
|
||||
--> tests/compile-fail/input_struct_incompatibles.rs:15:27
|
||||
|
|
||||
15 | #[salsa::input(jar = Jar, db = Db)]
|
||||
| ^^
|
||||
|
||||
error: unrecognized option `recover_fn`
|
||||
--> tests/compile-fail/input_struct_incompatibles.rs:18:27
|
||||
|
|
||||
18 | #[salsa::input(jar = Jar, recover_fn = recover)]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `lru` option not allowed here
|
||||
--> tests/compile-fail/input_struct_incompatibles.rs:21:27
|
||||
|
|
||||
21 | #[salsa::input(jar = Jar, lru =12)]
|
||||
| ^^^
|
||||
|
||||
error[E0412]: cannot find type `InputWithRetRef` in this scope
|
||||
--> tests/compile-fail/input_struct_incompatibles.rs:2:12
|
||||
|
|
||||
2 | struct Jar(InputWithRetRef, InputWithSpecify, InputNoWithEq, InputWithDb, InputWithRecover, InputWithLru);
|
||||
| ^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `InputWithSpecify` in this scope
|
||||
--> tests/compile-fail/input_struct_incompatibles.rs:2:29
|
||||
|
|
||||
2 | struct Jar(InputWithRetRef, InputWithSpecify, InputNoWithEq, InputWithDb, InputWithRecover, InputWithLru);
|
||||
| ^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `InputNoWithEq` in this scope
|
||||
--> tests/compile-fail/input_struct_incompatibles.rs:2:47
|
||||
|
|
||||
2 | struct Jar(InputWithRetRef, InputWithSpecify, InputNoWithEq, InputWithDb, InputWithRecover, InputWithLru);
|
||||
| ^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `InputWithDb` in this scope
|
||||
--> tests/compile-fail/input_struct_incompatibles.rs:2:62
|
||||
|
|
||||
2 | struct Jar(InputWithRetRef, InputWithSpecify, InputNoWithEq, InputWithDb, InputWithRecover, InputWithLru);
|
||||
| ^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `InputWithRecover` in this scope
|
||||
--> tests/compile-fail/input_struct_incompatibles.rs:2:75
|
||||
|
|
||||
2 | struct Jar(InputWithRetRef, InputWithSpecify, InputNoWithEq, InputWithDb, InputWithRecover, InputWithLru);
|
||||
| ^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `InputWithLru` in this scope
|
||||
--> tests/compile-fail/input_struct_incompatibles.rs:2:93
|
||||
|
|
||||
2 | struct Jar(InputWithRetRef, InputWithSpecify, InputNoWithEq, InputWithDb, InputWithRecover, InputWithLru);
|
||||
| ^^^^^^^^^^^^ not found in this scope
|
|
@ -0,0 +1,36 @@
|
|||
#[salsa::jar(db = Db)]
|
||||
struct Jar(InternedWithRetRef, InternedWithSpecify, InternedWithNoEq, InternedWithDb, InternedWithRecover, InternedWithLru);
|
||||
|
||||
trait Db: salsa::DbWithJar<Jar> {}
|
||||
|
||||
|
||||
#[salsa::interned(jar = Jar, return_ref)]
|
||||
struct InternedWithRetRef {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::interned(jar = Jar, specify)]
|
||||
struct InternedWithSpecify {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::interned(jar = Jar, no_eq)]
|
||||
struct InternedWithNoEq {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::interned(jar = Jar, db = Db)]
|
||||
struct InternedWithDb {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::interned(jar = Jar, recover_fn = recover)]
|
||||
struct InternedWithRecover {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::interned(jar = Jar, lru =12)]
|
||||
struct InternedWithLru {
|
||||
field: u32,
|
||||
}
|
||||
fn main() {}
|
|
@ -0,0 +1,71 @@
|
|||
error: `return_ref` option not allowed here
|
||||
--> tests/compile-fail/interned_struct_incompatibles.rs:7:30
|
||||
|
|
||||
7 | #[salsa::interned(jar = Jar, return_ref)]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `specify` option not allowed here
|
||||
--> tests/compile-fail/interned_struct_incompatibles.rs:12:30
|
||||
|
|
||||
12 | #[salsa::interned(jar = Jar, specify)]
|
||||
| ^^^^^^^
|
||||
|
||||
error: `no_eq` option not allowed here
|
||||
--> tests/compile-fail/interned_struct_incompatibles.rs:17:30
|
||||
|
|
||||
17 | #[salsa::interned(jar = Jar, no_eq)]
|
||||
| ^^^^^
|
||||
|
||||
error: `db` option not allowed here
|
||||
--> tests/compile-fail/interned_struct_incompatibles.rs:22:30
|
||||
|
|
||||
22 | #[salsa::interned(jar = Jar, db = Db)]
|
||||
| ^^
|
||||
|
||||
error: unrecognized option `recover_fn`
|
||||
--> tests/compile-fail/interned_struct_incompatibles.rs:27:30
|
||||
|
|
||||
27 | #[salsa::interned(jar = Jar, recover_fn = recover)]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `lru` option not allowed here
|
||||
--> tests/compile-fail/interned_struct_incompatibles.rs:32:30
|
||||
|
|
||||
32 | #[salsa::interned(jar = Jar, lru =12)]
|
||||
| ^^^
|
||||
|
||||
error[E0412]: cannot find type `InternedWithRetRef` in this scope
|
||||
--> tests/compile-fail/interned_struct_incompatibles.rs:2:12
|
||||
|
|
||||
2 | struct Jar(InternedWithRetRef, InternedWithSpecify, InternedWithNoEq, InternedWithDb, InternedWithRecover, InternedWithLru);
|
||||
| ^^^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `InternedWithSpecify` in this scope
|
||||
--> tests/compile-fail/interned_struct_incompatibles.rs:2:32
|
||||
|
|
||||
2 | struct Jar(InternedWithRetRef, InternedWithSpecify, InternedWithNoEq, InternedWithDb, InternedWithRecover, InternedWithLru);
|
||||
| ^^^^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `InternedWithNoEq` in this scope
|
||||
--> tests/compile-fail/interned_struct_incompatibles.rs:2:53
|
||||
|
|
||||
2 | struct Jar(InternedWithRetRef, InternedWithSpecify, InternedWithNoEq, InternedWithDb, InternedWithRecover, InternedWithLru);
|
||||
| ^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `InternedWithDb` in this scope
|
||||
--> tests/compile-fail/interned_struct_incompatibles.rs:2:71
|
||||
|
|
||||
2 | struct Jar(InternedWithRetRef, InternedWithSpecify, InternedWithNoEq, InternedWithDb, InternedWithRecover, InternedWithLru);
|
||||
| ^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `InternedWithRecover` in this scope
|
||||
--> tests/compile-fail/interned_struct_incompatibles.rs:2:87
|
||||
|
|
||||
2 | struct Jar(InternedWithRetRef, InternedWithSpecify, InternedWithNoEq, InternedWithDb, InternedWithRecover, InternedWithLru);
|
||||
| ^^^^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `InternedWithLru` in this scope
|
||||
--> tests/compile-fail/interned_struct_incompatibles.rs:2:108
|
||||
|
|
||||
2 | struct Jar(InternedWithRetRef, InternedWithSpecify, InternedWithNoEq, InternedWithDb, InternedWithRecover, InternedWithLru);
|
||||
| ^^^^^^^^^^^^^^^ not found in this scope
|
33
salsa-2022-tests/tests/compile-fail/jars_incompatibles.rs
Normal file
33
salsa-2022-tests/tests/compile-fail/jars_incompatibles.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
#[salsa::jar(db = Db, return_ref)]
|
||||
struct JarWithRetRef(MyInput);
|
||||
|
||||
#[salsa::jar(db = Db, specify)]
|
||||
struct JarWithDb(MyInput);
|
||||
|
||||
|
||||
#[salsa::jar(db = Db, no_eq)]
|
||||
struct JarWithNoEq(MyInput);
|
||||
|
||||
#[salsa::jar(db = Db, jar = Jar)]
|
||||
struct JarWithJar(MyInput);
|
||||
|
||||
#[salsa::jar(db = Db, data = Data)]
|
||||
struct JarWithData(MyInput);
|
||||
|
||||
#[salsa::jar(db = Db, recovery_fn = recover)]
|
||||
struct JarWithRecover(MyInput);
|
||||
|
||||
#[salsa::jar(db = Db, lru = 32)]
|
||||
struct JarWithLru(MyInput);
|
||||
|
||||
#[salsa::jar(db = Db, constructor = JarConstructor)]
|
||||
struct JarWithConstructor(MyInput);
|
||||
|
||||
#[salsa::input(jar = Jar1)]
|
||||
struct MyInput {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
error: `return_ref` option not allowed here
|
||||
--> tests/compile-fail/jars_incompatibles.rs:1:23
|
||||
|
|
||||
1 | #[salsa::jar(db = Db, return_ref)]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `specify` option not allowed here
|
||||
--> tests/compile-fail/jars_incompatibles.rs:4:23
|
||||
|
|
||||
4 | #[salsa::jar(db = Db, specify)]
|
||||
| ^^^^^^^
|
||||
|
||||
error: `no_eq` option not allowed here
|
||||
--> tests/compile-fail/jars_incompatibles.rs:8:23
|
||||
|
|
||||
8 | #[salsa::jar(db = Db, no_eq)]
|
||||
| ^^^^^
|
||||
|
||||
error: `jar` option not allowed here
|
||||
--> tests/compile-fail/jars_incompatibles.rs:11:23
|
||||
|
|
||||
11 | #[salsa::jar(db = Db, jar = Jar)]
|
||||
| ^^^
|
||||
|
||||
error: `data` option not allowed here
|
||||
--> tests/compile-fail/jars_incompatibles.rs:14:23
|
||||
|
|
||||
14 | #[salsa::jar(db = Db, data = Data)]
|
||||
| ^^^^
|
||||
|
||||
error: `recovery_fn` option not allowed here
|
||||
--> tests/compile-fail/jars_incompatibles.rs:17:23
|
||||
|
|
||||
17 | #[salsa::jar(db = Db, recovery_fn = recover)]
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: `lru` option not allowed here
|
||||
--> tests/compile-fail/jars_incompatibles.rs:20:23
|
||||
|
|
||||
20 | #[salsa::jar(db = Db, lru = 32)]
|
||||
| ^^^
|
||||
|
||||
error: `constructor` option not allowed here
|
||||
--> tests/compile-fail/jars_incompatibles.rs:23:23
|
||||
|
|
||||
23 | #[salsa::jar(db = Db, constructor = JarConstructor)]
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error[E0412]: cannot find type `Jar1` in this scope
|
||||
--> tests/compile-fail/jars_incompatibles.rs:26:22
|
||||
|
|
||||
26 | #[salsa::input(jar = Jar1)]
|
||||
| ^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `Jar1` in this scope
|
||||
--> tests/compile-fail/jars_incompatibles.rs:26:22
|
||||
|
|
||||
26 | #[salsa::input(jar = Jar1)]
|
||||
| ^^^^ not found in this scope
|
||||
27 | struct MyInput {
|
||||
| - help: you might be missing a type parameter: `<Jar1>`
|
|
@ -0,0 +1,19 @@
|
|||
#[salsa::jar(db = Db)]
|
||||
struct Jar(InputWithBannedName1, InputWithBannedName2);
|
||||
|
||||
// Banned field name: `from`
|
||||
#[salsa::input]
|
||||
struct InputWithBannedName1 {
|
||||
from: u32,
|
||||
}
|
||||
|
||||
// Banned field name: `new`
|
||||
#[salsa::input]
|
||||
struct InputWithBannedName2 {
|
||||
new: u32,
|
||||
}
|
||||
|
||||
trait Db: salsa::DbWithJar<Jar> {}
|
||||
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,23 @@
|
|||
error: the field name `from` is disallowed in salsa structs
|
||||
--> tests/compile-fail/salsa_fields_incompatibles.rs:7:5
|
||||
|
|
||||
7 | from: u32,
|
||||
| ^^^^
|
||||
|
||||
error: the field name `new` is disallowed in salsa structs
|
||||
--> tests/compile-fail/salsa_fields_incompatibles.rs:13:5
|
||||
|
|
||||
13 | new: u32,
|
||||
| ^^^
|
||||
|
||||
error[E0412]: cannot find type `InputWithBannedName1` in this scope
|
||||
--> tests/compile-fail/salsa_fields_incompatibles.rs:2:12
|
||||
|
|
||||
2 | struct Jar(InputWithBannedName1, InputWithBannedName2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `InputWithBannedName2` in this scope
|
||||
--> tests/compile-fail/salsa_fields_incompatibles.rs:2:34
|
||||
|
|
||||
2 | struct Jar(InputWithBannedName1, InputWithBannedName2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
|
@ -0,0 +1,47 @@
|
|||
#[salsa::jar(db = Db)]
|
||||
struct Jar(MyInput, tracked_fn_with_data, tracked_fn_with_db, tracked_fn_with_constructor, tracked_fn_with_one_input, tracked_fn_with_receiver_not_applied_to_impl_block);
|
||||
|
||||
trait Db: salsa::DbWithJar<Jar> {}
|
||||
|
||||
#[salsa::input(jar = Jar)]
|
||||
struct MyInput {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
|
||||
#[salsa::tracked(jar = Jar, data = Data)]
|
||||
fn tracked_fn_with_data(db: &dyn Db, input: MyInput) -> u32 {
|
||||
input.field(db) * 2
|
||||
}
|
||||
|
||||
#[salsa::tracked(jar = Jar, db = Db)]
|
||||
fn tracked_fn_with_db(db: &dyn Db, input: MyInput) -> u32 {
|
||||
input.field(db) * 2
|
||||
}
|
||||
|
||||
#[salsa::tracked(jar = Jar, constructor = TrackedFn3)]
|
||||
fn tracked_fn_with_constructor(db: &dyn Db, input: MyInput) -> u32 {
|
||||
input.field(db) * 2
|
||||
}
|
||||
|
||||
|
||||
#[salsa::tracked(jar = Jar)]
|
||||
fn tracked_fn_with_one_input(db: &dyn Db) -> u32 {
|
||||
}
|
||||
|
||||
|
||||
#[salsa::tracked(jar = Jar)]
|
||||
fn tracked_fn_with_receiver_not_applied_to_impl_block(&self, db: &dyn Db) -> u32 {
|
||||
}
|
||||
|
||||
#[salsa::tracked(jar = Jar, specify)]
|
||||
fn tracked_fn_with_receiver_not_applied_to_impl_block(db: &dyn Db, input: MyInput, input: MyInput) -> u32 {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,64 @@
|
|||
error: `data` option not allowed here
|
||||
--> tests/compile-fail/tracked_fn_incompatibles.rs:12:29
|
||||
|
|
||||
12 | #[salsa::tracked(jar = Jar, data = Data)]
|
||||
| ^^^^
|
||||
|
||||
error: `db` option not allowed here
|
||||
--> tests/compile-fail/tracked_fn_incompatibles.rs:17:29
|
||||
|
|
||||
17 | #[salsa::tracked(jar = Jar, db = Db)]
|
||||
| ^^
|
||||
|
||||
error: `constructor` option not allowed here
|
||||
--> tests/compile-fail/tracked_fn_incompatibles.rs:22:29
|
||||
|
|
||||
22 | #[salsa::tracked(jar = Jar, constructor = TrackedFn3)]
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: #[salsa::tracked] must also be applied to the impl block for tracked methods
|
||||
--> tests/compile-fail/tracked_fn_incompatibles.rs:34:55
|
||||
|
|
||||
34 | fn tracked_fn_with_receiver_not_applied_to_impl_block(&self, db: &dyn Db) -> u32 {
|
||||
| ^
|
||||
|
||||
error: tracked function takes too many arguments to have its value set with `specify`
|
||||
--> tests/compile-fail/tracked_fn_incompatibles.rs:37:29
|
||||
|
|
||||
37 | #[salsa::tracked(jar = Jar, specify)]
|
||||
| ^^^^^^^
|
||||
|
||||
error[E0412]: cannot find type `tracked_fn_with_data` in this scope
|
||||
--> tests/compile-fail/tracked_fn_incompatibles.rs:2:21
|
||||
|
|
||||
2 | struct Jar(MyInput, tracked_fn_with_data, tracked_fn_with_db, tracked_fn_with_constructor, tracked_fn_with_one_input, tracked_fn_with_rec...
|
||||
| ^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `tracked_fn_with_db` in this scope
|
||||
--> tests/compile-fail/tracked_fn_incompatibles.rs:2:43
|
||||
|
|
||||
2 | struct Jar(MyInput, tracked_fn_with_data, tracked_fn_with_db, tracked_fn_with_constructor, tracked_fn_with_one_input, tracked_fn_with_rec...
|
||||
| ^^^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `tracked_fn_with_constructor` in this scope
|
||||
--> tests/compile-fail/tracked_fn_incompatibles.rs:2:63
|
||||
|
|
||||
2 | struct Jar(MyInput, tracked_fn_with_data, tracked_fn_with_db, tracked_fn_with_constructor, tracked_fn_with_one_input, tracked_fn_with_rec...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: a struct with a similar name exists: `tracked_fn_with_one_input`
|
||||
...
|
||||
28 | #[salsa::tracked(jar = Jar)]
|
||||
| ---------------------------- similarly named struct `tracked_fn_with_one_input` defined here
|
||||
|
||||
error[E0412]: cannot find type `tracked_fn_with_receiver_not_applied_to_impl_block` in this scope
|
||||
--> tests/compile-fail/tracked_fn_incompatibles.rs:2:119
|
||||
|
|
||||
2 | ...r, tracked_fn_with_one_input, tracked_fn_with_receiver_not_applied_to_impl_block);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> tests/compile-fail/tracked_fn_incompatibles.rs:29:46
|
||||
|
|
||||
29 | fn tracked_fn_with_one_input(db: &dyn Db) -> u32 {
|
||||
| ------------------------- ^^^ expected `u32`, found `()`
|
||||
| |
|
||||
| implicitly returns `()` as its body has no tail or `return` expression
|
|
@ -0,0 +1,74 @@
|
|||
#[salsa::jar(db = Db)]
|
||||
struct Jar(MyTracked);
|
||||
|
||||
|
||||
#[salsa::tracked]
|
||||
struct MyTracked {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::tracked(return_ref)]
|
||||
impl std::default::Default for MyTracked {
|
||||
fn default() -> Self {
|
||||
|
||||
}
|
||||
}
|
||||
#[salsa::tracked(specify)]
|
||||
impl std::default::Default for MyTracked {
|
||||
fn default() -> Self {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::tracked(no_eq)]
|
||||
impl std::default::Default for MyTracked {
|
||||
fn default() -> Self {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::tracked(data = Data)]
|
||||
impl std::default::Default for MyTracked {
|
||||
fn default() -> Self {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::tracked(db = Db)]
|
||||
impl std::default::Default for MyTracked {
|
||||
fn default() -> Self {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::tracked(recover_fn = recover)]
|
||||
impl std::default::Default for MyTracked {
|
||||
fn default() -> Self {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::tracked(lru = 32)]
|
||||
impl std::default::Default for MyTracked {
|
||||
fn default() -> Self {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::tracked(constructor = Constructor)]
|
||||
impl std::default::Default for MyTracked {
|
||||
fn default() -> Self {
|
||||
|
||||
}
|
||||
}
|
||||
#[salsa::tracked]
|
||||
impl std::default::Default for [MyTracked; 12] {
|
||||
fn default() -> Self {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
trait Db: salsa::DbWithJar<Jar> {}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,53 @@
|
|||
error: `return_ref` option not allowed here
|
||||
--> tests/compile-fail/tracked_impl_incompatibles.rs:10:18
|
||||
|
|
||||
10 | #[salsa::tracked(return_ref)]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `specify` option not allowed here
|
||||
--> tests/compile-fail/tracked_impl_incompatibles.rs:16:18
|
||||
|
|
||||
16 | #[salsa::tracked(specify)]
|
||||
| ^^^^^^^
|
||||
|
||||
error: `no_eq` option not allowed here
|
||||
--> tests/compile-fail/tracked_impl_incompatibles.rs:23:18
|
||||
|
|
||||
23 | #[salsa::tracked(no_eq)]
|
||||
| ^^^^^
|
||||
|
||||
error: `data` option not allowed here
|
||||
--> tests/compile-fail/tracked_impl_incompatibles.rs:30:18
|
||||
|
|
||||
30 | #[salsa::tracked(data = Data)]
|
||||
| ^^^^
|
||||
|
||||
error: `db` option not allowed here
|
||||
--> tests/compile-fail/tracked_impl_incompatibles.rs:37:18
|
||||
|
|
||||
37 | #[salsa::tracked(db = Db)]
|
||||
| ^^
|
||||
|
||||
error: unrecognized option `recover_fn`
|
||||
--> tests/compile-fail/tracked_impl_incompatibles.rs:44:18
|
||||
|
|
||||
44 | #[salsa::tracked(recover_fn = recover)]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `lru` option not allowed here
|
||||
--> tests/compile-fail/tracked_impl_incompatibles.rs:51:18
|
||||
|
|
||||
51 | #[salsa::tracked(lru = 32)]
|
||||
| ^^^
|
||||
|
||||
error: `constructor` option not allowed here
|
||||
--> tests/compile-fail/tracked_impl_incompatibles.rs:58:18
|
||||
|
|
||||
58 | #[salsa::tracked(constructor = Constructor)]
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: #[salsa::tracked] can only be applied to salsa structs
|
||||
--> tests/compile-fail/tracked_impl_incompatibles.rs:65:32
|
||||
|
|
||||
65 | impl std::default::Default for [MyTracked; 12] {
|
||||
| ^^^^^^^^^^^^^^^
|
|
@ -0,0 +1,21 @@
|
|||
#[salsa::jar(db = Db)]
|
||||
struct Jar(Tracked);
|
||||
|
||||
|
||||
#[salsa::tracked(jar = Jar)]
|
||||
struct Tracked {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
|
||||
impl Tracked {
|
||||
#[salsa::tracked]
|
||||
fn use_tracked(&self) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
trait Db: salsa::DbWithJar<Jar> {}
|
||||
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,5 @@
|
|||
error: #[salsa::tracked] must also be applied to the impl block for tracked methods
|
||||
--> tests/compile-fail/tracked_method_incompatibles.rs:13:20
|
||||
|
|
||||
13 | fn use_tracked(&self) {
|
||||
| ^
|
|
@ -0,0 +1,18 @@
|
|||
#[salsa::jar(db = Db)]
|
||||
struct Jar(MyInput, tracked_method_on_untracked_impl);
|
||||
|
||||
trait Db: salsa::DbWithJar<Jar> {}
|
||||
|
||||
#[salsa::input]
|
||||
struct MyInput {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
impl MyInput {
|
||||
#[salsa::tracked]
|
||||
fn tracked_method_on_untracked_impl(self, db: &dyn Db) -> u32 {
|
||||
input.field(db)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,11 @@
|
|||
error: #[salsa::tracked] must also be applied to the impl block for tracked methods
|
||||
--> tests/compile-fail/tracked_method_on_untracked_impl.rs:13:41
|
||||
|
|
||||
13 | fn tracked_method_on_untracked_impl(self, db: &dyn Db) -> u32 {
|
||||
| ^^^^
|
||||
|
||||
error[E0412]: cannot find type `tracked_method_on_untracked_impl` in this scope
|
||||
--> tests/compile-fail/tracked_method_on_untracked_impl.rs:2:21
|
||||
|
|
||||
2 | struct Jar(MyInput, tracked_method_on_untracked_impl);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
|
@ -0,0 +1,36 @@
|
|||
#[salsa::jar(db = Db)]
|
||||
struct Jar(TrackedWithRetRef, TrackedSructWithSpecify, TrackedStructWithNoEq, TrackedStructWithDb, TrackedStructWithRecover, TrackedStructWithLru);
|
||||
|
||||
trait Db: salsa::DbWithJar<Jar> {}
|
||||
|
||||
|
||||
#[salsa::tracked(jar = Jar, return_ref)]
|
||||
struct TrackedWithRetRef {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::tracked(jar = Jar, specify)]
|
||||
struct TrackedSructWithSpecify {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::tracked(jar = Jar, no_eq)]
|
||||
struct TrackedStructWithNoEq {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::tracked(jar = Jar, db = Db)]
|
||||
struct TrackedStructWithDb {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::tracked(jar = Jar, recover_fn = recover)]
|
||||
struct TrackedStructWithRecover {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::tracked(jar = Jar, lru =12)]
|
||||
struct TrackedStructWithLru {
|
||||
field: u32,
|
||||
}
|
||||
fn main() {}
|
|
@ -0,0 +1,71 @@
|
|||
error: `return_ref` option not allowed here
|
||||
--> tests/compile-fail/tracked_struct_incompatibles.rs:7:29
|
||||
|
|
||||
7 | #[salsa::tracked(jar = Jar, return_ref)]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `specify` option not allowed here
|
||||
--> tests/compile-fail/tracked_struct_incompatibles.rs:12:29
|
||||
|
|
||||
12 | #[salsa::tracked(jar = Jar, specify)]
|
||||
| ^^^^^^^
|
||||
|
||||
error: `no_eq` option not allowed here
|
||||
--> tests/compile-fail/tracked_struct_incompatibles.rs:17:29
|
||||
|
|
||||
17 | #[salsa::tracked(jar = Jar, no_eq)]
|
||||
| ^^^^^
|
||||
|
||||
error: `db` option not allowed here
|
||||
--> tests/compile-fail/tracked_struct_incompatibles.rs:22:29
|
||||
|
|
||||
22 | #[salsa::tracked(jar = Jar, db = Db)]
|
||||
| ^^
|
||||
|
||||
error: unrecognized option `recover_fn`
|
||||
--> tests/compile-fail/tracked_struct_incompatibles.rs:27:29
|
||||
|
|
||||
27 | #[salsa::tracked(jar = Jar, recover_fn = recover)]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `lru` option not allowed here
|
||||
--> tests/compile-fail/tracked_struct_incompatibles.rs:32:29
|
||||
|
|
||||
32 | #[salsa::tracked(jar = Jar, lru =12)]
|
||||
| ^^^
|
||||
|
||||
error[E0412]: cannot find type `TrackedWithRetRef` in this scope
|
||||
--> tests/compile-fail/tracked_struct_incompatibles.rs:2:12
|
||||
|
|
||||
2 | struct Jar(TrackedWithRetRef, TrackedSructWithSpecify, TrackedStructWithNoEq, TrackedStructWithDb, TrackedStructWithRecover, TrackedStruc...
|
||||
| ^^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `TrackedSructWithSpecify` in this scope
|
||||
--> tests/compile-fail/tracked_struct_incompatibles.rs:2:31
|
||||
|
|
||||
2 | struct Jar(TrackedWithRetRef, TrackedSructWithSpecify, TrackedStructWithNoEq, TrackedStructWithDb, TrackedStructWithRecover, TrackedStruc...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `TrackedStructWithNoEq` in this scope
|
||||
--> tests/compile-fail/tracked_struct_incompatibles.rs:2:56
|
||||
|
|
||||
2 | struct Jar(TrackedWithRetRef, TrackedSructWithSpecify, TrackedStructWithNoEq, TrackedStructWithDb, TrackedStructWithRecover, TrackedStruc...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `TrackedStructWithDb` in this scope
|
||||
--> tests/compile-fail/tracked_struct_incompatibles.rs:2:79
|
||||
|
|
||||
2 | struct Jar(TrackedWithRetRef, TrackedSructWithSpecify, TrackedStructWithNoEq, TrackedStructWithDb, TrackedStructWithRecover, TrackedStruc...
|
||||
| ^^^^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `TrackedStructWithRecover` in this scope
|
||||
--> tests/compile-fail/tracked_struct_incompatibles.rs:2:100
|
||||
|
|
||||
2 | ...rackedStructWithNoEq, TrackedStructWithDb, TrackedStructWithRecover, TrackedStructWithLru);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `TrackedStructWithLru` in this scope
|
||||
--> tests/compile-fail/tracked_struct_incompatibles.rs:2:126
|
||||
|
|
||||
2 | ...ackedStructWithDb, TrackedStructWithRecover, TrackedStructWithLru);
|
||||
| ^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
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);
|
||||
}
|
62
salsa-2022-tests/tests/tracked_method.rs
Normal file
62
salsa-2022-tests/tests/tracked_method.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
//! Test that a `tracked` fn on a `salsa::input`
|
||||
//! compiles and executes successfully.
|
||||
#![allow(warnings)]
|
||||
|
||||
#[salsa::jar(db = Db)]
|
||||
struct Jar(
|
||||
MyInput,
|
||||
MyInput_tracked_fn,
|
||||
MyInput_tracked_fn_ref,
|
||||
MyInput_TrackedTrait_tracked_trait_fn,
|
||||
);
|
||||
|
||||
trait Db: salsa::DbWithJar<Jar> {}
|
||||
|
||||
trait TrackedTrait {
|
||||
fn tracked_trait_fn(self, db: &dyn Db) -> u32;
|
||||
}
|
||||
|
||||
#[salsa::input]
|
||||
struct MyInput {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
impl MyInput {
|
||||
#[salsa::tracked]
|
||||
fn tracked_fn(self, db: &dyn Db) -> u32 {
|
||||
self.field(db) * 2
|
||||
}
|
||||
|
||||
#[salsa::tracked(return_ref)]
|
||||
fn tracked_fn_ref(self, db: &dyn Db) -> u32 {
|
||||
self.field(db) * 3
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
impl TrackedTrait for MyInput {
|
||||
#[salsa::tracked]
|
||||
fn tracked_trait_fn(self, db: &dyn Db) -> u32 {
|
||||
self.field(db) * 4
|
||||
}
|
||||
}
|
||||
|
||||
#[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();
|
||||
let object = MyInput::new(&mut db, 22);
|
||||
assert_eq!(object.tracked_fn(&db), 44);
|
||||
assert_eq!(*object.tracked_fn_ref(&db), 66);
|
||||
assert_eq!(object.tracked_trait_fn(&db), 88);
|
||||
}
|
Loading…
Reference in a new issue