mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-01-22 21:05:11 +00:00
removed AllowedMode trait and added compile-fail tests
This commit is contained in:
parent
3ca70e6b04
commit
25809151dd
13 changed files with 119 additions and 131 deletions
|
@ -40,15 +40,6 @@ impl crate::options::AllowedOptions for Accumulator {
|
|||
const CONSTRUCTOR_NAME: bool = false;
|
||||
}
|
||||
|
||||
impl crate::modes::AllowedModes for Accumulator {
|
||||
const TRACKED: bool = false;
|
||||
|
||||
const INPUT: bool = false;
|
||||
|
||||
const INTERNED: bool = false;
|
||||
|
||||
const ACCUMULATOR: bool = true;
|
||||
}
|
||||
|
||||
fn accumulator_contents(
|
||||
args: &Args,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use crate::modes::Mode;
|
||||
use crate::salsa_struct::{SalsaField, SalsaStruct};
|
||||
use proc_macro2::{Literal, TokenStream};
|
||||
|
||||
|
@ -11,19 +10,16 @@ pub(crate) fn input(
|
|||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let mode = Mode {
|
||||
..Default::default()
|
||||
};
|
||||
match SalsaStruct::new(args, input, mode).and_then(|el| InputStruct(el).generate_input()) {
|
||||
match SalsaStruct::new(args, input).and_then(|el| InputStruct(el).generate_input()) {
|
||||
Ok(s) => s.into(),
|
||||
Err(err) => err.into_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
||||
struct InputStruct(SalsaStruct<Self, Self>);
|
||||
struct InputStruct(SalsaStruct<Self>);
|
||||
|
||||
impl std::ops::Deref for InputStruct {
|
||||
type Target = SalsaStruct<Self, Self>;
|
||||
type Target = SalsaStruct<Self>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
|
@ -51,15 +47,7 @@ impl crate::options::AllowedOptions for InputStruct {
|
|||
const CONSTRUCTOR_NAME: bool = true;
|
||||
}
|
||||
|
||||
impl crate::modes::AllowedModes for InputStruct {
|
||||
const TRACKED: bool = false;
|
||||
|
||||
const INPUT: bool = true;
|
||||
|
||||
const INTERNED: bool = false;
|
||||
|
||||
const ACCUMULATOR: bool = false;
|
||||
}
|
||||
|
||||
impl InputStruct {
|
||||
fn generate_input(&self) -> syn::Result<TokenStream> {
|
||||
|
@ -89,14 +77,6 @@ impl InputStruct {
|
|||
fn validate_input(&self) -> syn::Result<()> {
|
||||
// check for dissalowed fields
|
||||
self.disallow_id_fields("input")?;
|
||||
|
||||
// check if an input struct labeled singleton truly has one field
|
||||
if self.0.is_isingleton() && self.0.num_fields() != 1 {
|
||||
return Err(syn::Error::new(
|
||||
self.0.struct_span(),
|
||||
format!("`Singleton` input mut have only one field"),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use crate::modes::Mode;
|
||||
use crate::salsa_struct::SalsaStruct;
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
|
@ -14,20 +13,17 @@ pub(crate) fn interned(
|
|||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let mode = Mode {
|
||||
..Default::default()
|
||||
};
|
||||
match SalsaStruct::new(args, input, mode).and_then(|el| InternedStruct(el).generate_interned())
|
||||
match SalsaStruct::new(args, input).and_then(|el| InternedStruct(el).generate_interned())
|
||||
{
|
||||
Ok(s) => s.into(),
|
||||
Err(err) => err.into_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
||||
struct InternedStruct(SalsaStruct<Self, Self>);
|
||||
struct InternedStruct(SalsaStruct<Self>);
|
||||
|
||||
impl std::ops::Deref for InternedStruct {
|
||||
type Target = SalsaStruct<Self, Self>;
|
||||
type Target = SalsaStruct<Self>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
|
@ -56,15 +52,6 @@ impl crate::options::AllowedOptions for InternedStruct {
|
|||
const CONSTRUCTOR_NAME: bool = true;
|
||||
}
|
||||
|
||||
impl crate::modes::AllowedModes for InternedStruct {
|
||||
const TRACKED: bool = false;
|
||||
|
||||
const INPUT: bool = true;
|
||||
|
||||
const INTERNED: bool = false;
|
||||
|
||||
const ACCUMULATOR: bool = false;
|
||||
}
|
||||
|
||||
impl InternedStruct {
|
||||
fn generate_interned(&self) -> syn::Result<TokenStream> {
|
||||
|
|
|
@ -36,7 +36,6 @@ mod db;
|
|||
mod input;
|
||||
mod interned;
|
||||
mod jar;
|
||||
mod modes;
|
||||
mod options;
|
||||
mod salsa_struct;
|
||||
mod tracked;
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
/// The four possible modes of Salsa structs
|
||||
/// Salsa structs asre generic over AllowedModes.
|
||||
pub(crate) trait AllowedModes {
|
||||
const TRACKED: bool;
|
||||
const INPUT: bool;
|
||||
const INTERNED: bool;
|
||||
const ACCUMULATOR: bool;
|
||||
}
|
||||
|
||||
///
|
||||
pub(crate) struct Mode<M: AllowedModes> {
|
||||
pub(super) phantom: PhantomData<M>,
|
||||
}
|
||||
|
||||
impl<M: AllowedModes> Default for Mode<M> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: AllowedModes> Mode<M> {
|
||||
pub(crate) fn singleton_allowed(&self) -> bool {
|
||||
M::INPUT
|
||||
}
|
||||
}
|
|
@ -2,12 +2,13 @@ use std::marker::PhantomData;
|
|||
|
||||
use syn::{ext::IdentExt, spanned::Spanned};
|
||||
|
||||
|
||||
/// "Options" are flags that can be supplied to the various salsa related
|
||||
/// macros. They are listed like `(ref, no_eq, foo=bar)` etc. The commas
|
||||
/// are required and trailing commas are permitted. The options accepted
|
||||
/// for any particular location are configured via the `AllowedOptions`
|
||||
/// trait.
|
||||
pub(crate) struct Options<A: AllowedOptions> {
|
||||
pub(crate) struct Options<A: AllowedOptions > {
|
||||
/// The `return_ref` option is used to signal that field/return type is "by ref"
|
||||
///
|
||||
/// If this is `Some`, the value is the `ref` identifier.
|
||||
|
@ -116,7 +117,7 @@ impl<A: AllowedOptions> Options<A> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<A: AllowedOptions> syn::parse::Parse for Options<A> {
|
||||
impl<A: AllowedOptions > syn::parse::Parse for Options<A> {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let mut options = Options::default();
|
||||
|
||||
|
|
|
@ -25,46 +25,39 @@
|
|||
//! * data method `impl Foo { fn data(&self, db: &dyn crate::Db) -> FooData { FooData { f: self.f(db), ... } } }`
|
||||
//! * this could be optimized, particularly for interned fields
|
||||
|
||||
use crate::modes::Mode;
|
||||
use crate::{
|
||||
configuration,
|
||||
modes::AllowedModes,
|
||||
options::{AllowedOptions, Options},
|
||||
};
|
||||
use heck::ToUpperCamelCase;
|
||||
use proc_macro2::{Ident, Literal, Span, TokenStream};
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
pub(crate) struct SalsaStruct<A: AllowedOptions, M: AllowedModes> {
|
||||
pub(crate) struct SalsaStruct<A: AllowedOptions> {
|
||||
args: Options<A>,
|
||||
_mode: Mode<M>,
|
||||
struct_item: syn::ItemStruct,
|
||||
fields: Vec<SalsaField>,
|
||||
}
|
||||
|
||||
const BANNED_FIELD_NAMES: &[&str] = &["from", "new"];
|
||||
|
||||
impl<A: AllowedOptions, M: AllowedModes> SalsaStruct<A, M> {
|
||||
impl<A: AllowedOptions> SalsaStruct<A> {
|
||||
pub(crate) fn new(
|
||||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
mode: Mode<M>,
|
||||
) -> syn::Result<Self> {
|
||||
let struct_item = syn::parse(input)?;
|
||||
Self::with_struct(args, struct_item, mode)
|
||||
Self::with_struct(args, struct_item)
|
||||
}
|
||||
|
||||
pub(crate) fn with_struct(
|
||||
args: proc_macro::TokenStream,
|
||||
struct_item: syn::ItemStruct,
|
||||
mode: Mode<M>,
|
||||
) -> syn::Result<Self> {
|
||||
let args: Options<A> = syn::parse(args)?;
|
||||
let fields = Self::extract_options(&struct_item)?;
|
||||
check_singleton(&mode, args.singleton.as_ref(), struct_item.span())?;
|
||||
Ok(Self {
|
||||
args,
|
||||
_mode: mode,
|
||||
struct_item,
|
||||
fields,
|
||||
})
|
||||
|
@ -133,13 +126,6 @@ impl<A: AllowedOptions, M: AllowedModes> SalsaStruct<A, M> {
|
|||
self.args.singleton.is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn num_fields(&self) -> usize {
|
||||
self.fields.len()
|
||||
}
|
||||
|
||||
pub(crate) fn struct_span(&self) -> Span {
|
||||
self.struct_item.span()
|
||||
}
|
||||
|
||||
pub(crate) fn db_dyn_ty(&self) -> syn::Type {
|
||||
let jar_ty = self.jar_ty();
|
||||
|
@ -450,17 +436,3 @@ impl SalsaField {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn check_singleton<M: AllowedModes>(
|
||||
mode: &Mode<M>,
|
||||
sing: Option<&syn::Ident>,
|
||||
s_span: Span,
|
||||
) -> syn::Result<()> {
|
||||
if !mode.singleton_allowed() && sing.is_some() {
|
||||
Err(syn::Error::new(
|
||||
s_span,
|
||||
format!("`Singleton` not allowed for this Salsa struct type"),
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use proc_macro2::{Literal, TokenStream};
|
||||
|
||||
use crate::{
|
||||
modes::Mode,
|
||||
salsa_struct::{SalsaField, SalsaStruct},
|
||||
};
|
||||
|
||||
|
@ -14,10 +13,7 @@ pub(crate) fn tracked(
|
|||
args: proc_macro::TokenStream,
|
||||
struct_item: syn::ItemStruct,
|
||||
) -> proc_macro::TokenStream {
|
||||
let mode = Mode {
|
||||
..Default::default()
|
||||
};
|
||||
match SalsaStruct::with_struct(args, struct_item, mode)
|
||||
match SalsaStruct::with_struct(args, struct_item)
|
||||
.and_then(|el| TrackedStruct(el).generate_tracked())
|
||||
{
|
||||
Ok(s) => s.into(),
|
||||
|
@ -25,10 +21,10 @@ pub(crate) fn tracked(
|
|||
}
|
||||
}
|
||||
|
||||
struct TrackedStruct(SalsaStruct<Self, Self>);
|
||||
struct TrackedStruct(SalsaStruct<Self>);
|
||||
|
||||
impl std::ops::Deref for TrackedStruct {
|
||||
type Target = SalsaStruct<Self, Self>;
|
||||
type Target = SalsaStruct<Self>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
|
@ -57,15 +53,7 @@ impl crate::options::AllowedOptions for TrackedStruct {
|
|||
const CONSTRUCTOR_NAME: bool = true;
|
||||
}
|
||||
|
||||
impl crate::modes::AllowedModes for TrackedStruct {
|
||||
const TRACKED: bool = true;
|
||||
|
||||
const INPUT: bool = false;
|
||||
|
||||
const INTERNED: bool = false;
|
||||
|
||||
const ACCUMULATOR: bool = false;
|
||||
}
|
||||
|
||||
impl TrackedStruct {
|
||||
fn generate_tracked(&self) -> syn::Result<TokenStream> {
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
//! Compile Singleton struct test:
|
||||
//!
|
||||
//! Singleton flags are only allowed for input structs. If applied on any other Salsa struct compilation must fail
|
||||
|
||||
use salsa_2022_tests::{HasLogger, Logger};
|
||||
|
||||
use test_log::test;
|
||||
|
||||
#[salsa::jar(db = Db)]
|
||||
struct Jar(MyInput, MyTracked, Integers, create_tracked_structs );
|
||||
|
||||
trait Db: salsa::DbWithJar<Jar> + HasLogger {}
|
||||
|
||||
#[salsa::input(singleton)]
|
||||
struct MyInput {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
|
||||
#[salsa::tracked(singleton)]
|
||||
struct MyTracked {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
|
||||
#[salsa::tracked(singleton)]
|
||||
fn create_tracked_structs(db: &dyn Db, input: MyInput) -> Vec<MyTracked> {
|
||||
(0..input.field(db))
|
||||
.map(|i| MyTracked::new(db, i))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[salsa::accumulator(singleton)]
|
||||
struct Integers(u32);
|
||||
|
||||
|
||||
#[salsa::db(Jar)]
|
||||
#[derive(Default)]
|
||||
struct Database {
|
||||
storage: salsa::Storage<Self>,
|
||||
logger: Logger,
|
||||
}
|
||||
|
||||
impl salsa::Database for Database {}
|
||||
|
||||
impl Db for Database {}
|
||||
|
||||
impl HasLogger for Database {
|
||||
fn logger(&self) -> &Logger {
|
||||
&self.logger
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,43 @@
|
|||
error: `singleton` option not allowed here
|
||||
--> tests/compile-fail/singleton_only_for_input.rs:20:18
|
||||
|
|
||||
20 | #[salsa::tracked(singleton)]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `singleton` option not allowed here
|
||||
--> tests/compile-fail/singleton_only_for_input.rs:26:18
|
||||
|
|
||||
26 | #[salsa::tracked(singleton)]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `singleton` option not allowed here
|
||||
--> tests/compile-fail/singleton_only_for_input.rs:33:22
|
||||
|
|
||||
33 | #[salsa::accumulator(singleton)]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error[E0412]: cannot find type `MyTracked` in this scope
|
||||
--> tests/compile-fail/singleton_only_for_input.rs:10:21
|
||||
|
|
||||
10 | struct Jar(MyInput, MyTracked, Integers, create_tracked_structs );
|
||||
| ^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `Integers` in this scope
|
||||
--> tests/compile-fail/singleton_only_for_input.rs:10:32
|
||||
|
|
||||
10 | struct Jar(MyInput, MyTracked, Integers, create_tracked_structs );
|
||||
| ^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0412]: cannot find type `create_tracked_structs` in this scope
|
||||
--> tests/compile-fail/singleton_only_for_input.rs:10:42
|
||||
|
|
||||
10 | struct Jar(MyInput, MyTracked, Integers, create_tracked_structs );
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
warning: unused import: `test_log::test`
|
||||
--> tests/compile-fail/singleton_only_for_input.rs:7:5
|
||||
|
|
||||
7 | use test_log::test;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unused_imports)]` on by default
|
|
@ -17,6 +17,7 @@ struct MyTracked {
|
|||
field: u32,
|
||||
}
|
||||
|
||||
|
||||
#[salsa::tracked(jar = Jar, specify)]
|
||||
fn tracked_fn(db: &dyn Db, input: MyInterned) -> MyTracked {
|
||||
MyTracked::new(db, input.field(db) * 2)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
error[E0277]: the trait bound `MyInterned: TrackedStructInDb<dyn Db>` is not satisfied
|
||||
--> tests/compile-fail/specify-does-not-work-if-the-key-is-a-salsa-interned.rs:21:28
|
||||
--> tests/compile-fail/specify-does-not-work-if-the-key-is-a-salsa-interned.rs:22:28
|
||||
|
|
||||
20 | #[salsa::tracked(jar = Jar, specify)]
|
||||
21 | #[salsa::tracked(jar = Jar, specify)]
|
||||
| ------------------------------------- required by a bound introduced by this call
|
||||
21 | fn tracked_fn(db: &dyn Db, input: MyInterned) -> MyTracked {
|
||||
22 | fn tracked_fn(db: &dyn Db, input: MyInterned) -> MyTracked {
|
||||
| ^^^^^ the trait `TrackedStructInDb<dyn Db>` is not implemented for `MyInterned`
|
||||
|
|
||||
= help: the trait `TrackedStructInDb<DB>` is implemented for `MyTracked`
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Basic deletion test:
|
||||
//! Basic Singleton struct test:
|
||||
//!
|
||||
//! * entities not created in a revision are deleted, as is any memoized data keyed on them.
|
||||
//! Singleton structs are created only once. Subsequent `get`s and `new`s after creation return the same `Id`.
|
||||
|
||||
use salsa_2022_tests::{HasLogger, Logger};
|
||||
|
||||
|
|
Loading…
Reference in a new issue