Merge branch 'salsa-rs:master' into master

This commit is contained in:
Mihail Mihov 2022-08-06 21:52:26 +03:00 committed by GitHub
commit 5d1ff35f6d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
64 changed files with 236 additions and 55 deletions

View file

@ -33,6 +33,10 @@ jobs:
toolchain: ${{ matrix.rust }}
components: rustfmt, clippy
default: true
- uses: actions-rs/cargo@v1
with:
command: check
args: --all
- uses: actions-rs/cargo@v1
with:
command: test

View file

@ -33,8 +33,8 @@ insta = "1.8.0"
[workspace]
members = [
"components/salsa-macros",
"components/salsa-entity-mock",
"components/salsa-entity-macros",
"components/salsa-2022",
"components/salsa-2022-macros",
"calc-example/calc",
"salsa-2022-tests"
]

View file

@ -1,3 +1,3 @@
> ⚠️ **IN-PROGRESS VERSION OF SALSA.** ⚠️
>
> This page describes the unreleased "Salsa 2022" version, which is a major departure from older versions of salsa. The code here works but is only available on github and from the `salsa-entity-mock` crate.
> This page describes the unreleased "Salsa 2022" version, which is a major departure from older versions of salsa. The code here works but is only available on github and from the `salsa-2022` crate.

View file

@ -33,7 +33,7 @@ In addition to the struct itself, we must add an impl of `salsa::Database`:
If you want to permit accessing your database from multiple threads at once, then you also need to implement the `ParallelDatabase` trait:
```rust
{{#include ../../../calc-example/calc/src/db.rs:db_impl}}
{{#include ../../../calc-example/calc/src/db.rs:par_db_impl}}
```
## Implementing the `Default` trait

View file

@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
salsa = { path = "../../components/salsa-entity-mock", package = "salsa-entity-mock" }
salsa = { path = "../../components/salsa-2022", package = "salsa-2022" }
ordered-float = "3.0"
[dev-dependencies]

View file

@ -1,5 +1,5 @@
[package]
name = "salsa-entity-macros"
name = "salsa-2022-macros"
version = "0.1.0"
edition = "2021"

View file

@ -345,11 +345,3 @@ impl SalsaField {
!self.has_no_eq_attr
}
}
/// True if this an attribute that salsa permits users to attach to
/// entity/interned fields.
fn is_entity_like_field_attribute(a: &syn::Attribute) -> bool {
FIELD_OPTION_ATTRIBUTES
.iter()
.any(|(fa, _)| a.path.is_ident(fa))
}

View file

@ -370,10 +370,9 @@ fn specify_fn(
item_fn: &syn::ItemFn,
config_ty: &syn::Type,
) -> syn::Result<Option<syn::ImplItemMethod>> {
let specify = match &args.specify {
Some(s) => s,
None => return Ok(None),
};
if args.specify.is_none() {
return Ok(None);
}
// `specify` has the same signature as the original,
// but it takes a value arg and has no return type.

View file

@ -1,5 +1,5 @@
[package]
name = "salsa-entity-mock"
name = "salsa-2022"
version = "0.1.0"
edition = "2021"
@ -16,4 +16,4 @@ crossbeam-utils = { version = "0.8", default-features = false }
log = "0.4.5"
parking_lot = "0.11.0"
smallvec = "1.0.0"
salsa-entity-macros = { path = "../salsa-entity-macros" }
salsa-2022-macros = { path = "../salsa-2022-macros" }

View file

@ -67,7 +67,7 @@ pub trait ParallelDatabase: Database + Send {
/// fn snapshot(&self) -> Snapshot<Self> {
/// Snapshot::new(
/// MyDatabaseType {
/// runtime: self.runtime.snapshot(self),
/// runtime: self.storage.snapshot(),
/// other_field: self.other_field.clone(),
/// }
/// )

View file

@ -41,9 +41,9 @@ pub use self::storage::DbWithJar;
pub use self::storage::Storage;
pub use self::tracked_struct::TrackedStructData;
pub use self::tracked_struct::TrackedStructId;
pub use salsa_entity_macros::accumulator;
pub use salsa_entity_macros::db;
pub use salsa_entity_macros::input;
pub use salsa_entity_macros::interned;
pub use salsa_entity_macros::jar;
pub use salsa_entity_macros::tracked;
pub use salsa_2022_macros::accumulator;
pub use salsa_2022_macros::db;
pub use salsa_2022_macros::input;
pub use salsa_2022_macros::interned;
pub use salsa_2022_macros::jar;
pub use salsa_2022_macros::tracked;

View file

@ -6,11 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
salsa = { path = "../components/salsa-entity-mock", package = "salsa-entity-mock" }
[dev-dependencies]
salsa = { path = "../components/salsa-2022", package = "salsa-2022" }
expect-test = "1.4.0"
[[bin]]
name = "salsa-2022-tests"
path = "main.rs"

View file

@ -1,8 +0,0 @@
//! This crate has the beginning of various unit tests on salsa 2022
//! code.
mod tracked_fn_on_input;
mod tracked_fn_on_tracked;
mod tracked_fn_on_tracked_specify;
fn main() {}

View file

@ -0,0 +1,24 @@
/// Utility for tests that lets us log when notable events happen.
#[derive(Default)]
pub struct Logger {
logs: std::sync::Mutex<Vec<String>>,
}
/// Trait implemented by databases that lets them log events.
pub trait HasLogger {
/// Return a reference to the logger from the database.
fn logger(&self) -> &Logger;
/// Log an event from inside a tracked function.
fn push_log(&self, string: String) {
self.logger().logs.lock().unwrap().push(string);
}
/// Asserts what the (formatted) logs should look like,
/// clearing the logged events. This takes `&mut self` because
/// it is meant to be run from outside any tracked functions.
fn assert_logs(&mut self, expected: expect_test::Expect) {
let logs = std::mem::replace(&mut *self.logger().logs.lock().unwrap(), vec![]);
expected.assert_eq(&format!("{:#?}", logs));
}
}

View file

@ -0,0 +1,85 @@
//! Test that a `tracked` fn on a `salsa::input`
//! compiles and executes successfully.
#![allow(dead_code)]
use salsa_2022_tests::{HasLogger, Logger};
use expect_test::expect;
#[salsa::jar(db = Db)]
struct Jar(MyInput, MyTracked, final_result, intermediate_result);
trait Db: salsa::DbWithJar<Jar> + HasLogger {}
#[salsa::input(jar = Jar)]
struct MyInput {
field: u32,
}
#[salsa::tracked(jar = Jar)]
fn final_result(db: &dyn Db, input: MyInput) -> u32 {
db.push_log(format!("final_result({:?})", input));
intermediate_result(db, input).field(db) * 2
}
#[salsa::tracked(jar = Jar)]
struct MyTracked {
field: u32,
}
#[salsa::tracked(jar = Jar)]
fn intermediate_result(db: &dyn Db, input: MyInput) -> MyTracked {
db.push_log(format!("intermediate_result({:?})", input));
MyTracked::new(db, input.field(db) / 2)
}
#[salsa::db(Jar)]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
impl salsa::Database for Database {
fn salsa_runtime(&self) -> &salsa::Runtime {
self.storage.runtime()
}
}
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test]
fn execute() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 22);
assert_eq!(final_result(&db, input), 22);
db.assert_logs(expect![[r#"
[
"final_result(MyInput(Id { value: 1 }))",
"intermediate_result(MyInput(Id { value: 1 }))",
]"#]]);
// Intermediate result is the same, so final result does
// not need to be recomputed:
input.set_field(&mut db, 23);
assert_eq!(final_result(&db, input), 22);
db.assert_logs(expect![[r#"
[
"intermediate_result(MyInput(Id { value: 1 }))",
]"#]]);
input.set_field(&mut db, 24);
assert_eq!(final_result(&db, input), 24);
db.assert_logs(expect![[r#"
[
"intermediate_result(MyInput(Id { value: 1 }))",
"final_result(MyInput(Id { value: 1 }))",
]"#]]);
}

View file

@ -1,5 +1,6 @@
//! Test that a `tracked` fn on a `salsa::input`
//! compiles and executes successfully.
#![allow(warnings)]
#[salsa::jar(db = Db)]
struct Jar(MyInput, tracked_fn);

View file

@ -1,5 +1,6 @@
//! Test that a `tracked` fn on a `salsa::input`
//! compiles and executes successfully.
#![allow(dead_code)]
#[salsa::jar(db = Db)]
struct Jar(MyInput, MyTracked, tracked_fn);
@ -21,22 +22,22 @@ fn tracked_fn(db: &dyn Db, input: MyInput) -> MyTracked {
MyTracked::new(db, input.field(db) * 2)
}
#[salsa::db(Jar)]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
impl salsa::Database for Database {
fn salsa_runtime(&self) -> &salsa::Runtime {
self.storage.runtime()
}
}
impl Db for Database {}
#[test]
fn execute() {
#[salsa::db(Jar)]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
impl salsa::Database for Database {
fn salsa_runtime(&self) -> &salsa::Runtime {
self.storage.runtime()
}
}
impl Db for Database {}
let mut db = Database::default();
let input = MyInput::new(&mut db, 22);
assert_eq!(tracked_fn(&db, input).field(&db), 44);

View file

@ -1,5 +1,6 @@
//! Test that a `tracked` fn on a `salsa::input`
//! compiles and executes successfully.
#![allow(warnings)]
#[salsa::jar(db = Db)]
struct Jar(MyInput, MyTracked, tracked_fn, tracked_fn_extra);

View file

@ -0,0 +1,87 @@
//! Test that a `tracked` fn on a `salsa::input`
//! compiles and executes successfully.
#![allow(dead_code)]
use salsa_2022_tests::{HasLogger, Logger};
use expect_test::expect;
#[salsa::jar(db = Db)]
struct Jar(MyInput, MyTracked, final_result, intermediate_result);
trait Db: salsa::DbWithJar<Jar> + HasLogger {}
#[salsa::input(jar = Jar)]
struct MyInput {
field: u32,
}
#[salsa::tracked(jar = Jar)]
fn final_result(db: &dyn Db, input: MyInput) -> u32 {
db.push_log(format!("final_result({:?})", input));
intermediate_result(db, input).field(db) * 2
}
#[salsa::tracked(jar = Jar)]
struct MyTracked {
field: u32,
}
#[salsa::tracked(jar = Jar)]
fn intermediate_result(db: &dyn Db, input: MyInput) -> MyTracked {
db.push_log(format!("intermediate_result({:?})", input));
let tracked = MyTracked::new(db, input.field(db) / 2);
let _ = tracked.field(db); // read the field of an entity we created
tracked
}
#[salsa::db(Jar)]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
impl salsa::Database for Database {
fn salsa_runtime(&self) -> &salsa::Runtime {
self.storage.runtime()
}
}
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test]
fn execute() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 22);
assert_eq!(final_result(&db, input), 22);
db.assert_logs(expect![[r#"
[
"final_result(MyInput(Id { value: 1 }))",
"intermediate_result(MyInput(Id { value: 1 }))",
]"#]]);
// Intermediate result is the same, so final result does
// not need to be recomputed:
input.set_field(&mut db, 23);
assert_eq!(final_result(&db, input), 22);
db.assert_logs(expect![[r#"
[
"intermediate_result(MyInput(Id { value: 1 }))",
]"#]]);
input.set_field(&mut db, 24);
assert_eq!(final_result(&db, input), 24);
db.assert_logs(expect![[r#"
[
"intermediate_result(MyInput(Id { value: 1 }))",
"final_result(MyInput(Id { value: 1 }))",
]"#]]);
}