removed AllowedMode trait and added compile-fail tests

This commit is contained in:
OLUWAMUYIWA 2022-09-02 13:09:28 +01:00
parent 3ca70e6b04
commit 25809151dd
13 changed files with 119 additions and 131 deletions

View file

@ -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,

View file

@ -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(())
}

View file

@ -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> {

View file

@ -36,7 +36,6 @@ mod db;
mod input;
mod interned;
mod jar;
mod modes;
mod options;
mod salsa_struct;
mod tracked;

View file

@ -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
}
}

View file

@ -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();

View file

@ -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(())
}
}

View file

@ -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> {

View file

@ -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() {}

View file

@ -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

View file

@ -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)

View file

@ -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`

View file

@ -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};