This commit is contained in:
Niko Matsakis 2024-07-17 06:41:56 -04:00
parent afd7bcfa78
commit 7443277381
19 changed files with 113 additions and 126 deletions

View file

@ -12,6 +12,7 @@
//! from a submodule is to use multiple crates, hence the existence
//! of this crate.
mod macro_if;
mod maybe_backdate;
mod maybe_clone;
mod setup_accumulator_impl;

View file

@ -0,0 +1,9 @@
#[macro_export]
macro_rules! macro_if {
(true => $($t:tt)*) => {
$($t)*
};
(false => $($t:tt)*) => {
};
}

View file

@ -35,10 +35,11 @@ macro_rules! setup_input_struct {
// Number of fields
num_fields: $N:literal,
// Control customization: each path below either appears or doesn't.
customized: [
$($DebugTrait:path)?, // std::fmt::Debug
],
// If true, this is a singleton input.
is_singleton: $is_singleton:tt,
// If true, generate a debug impl.
generate_debug_impl: $generate_debug_impl:tt,
// Annoyingly macro-rules hygiene does not extend to items defined in the macro.
// We have the procedural macro generate names for those items that are
@ -64,6 +65,7 @@ macro_rules! setup_input_struct {
impl $zalsa_struct::Configuration for $Configuration {
const DEBUG_NAME: &'static str = stringify!($Struct);
const FIELD_DEBUG_NAMES: &'static [&'static str] = &[$(stringify!($field_id)),*];
const IS_SINGLETON: bool = $is_singleton;
/// The input struct (which wraps an `Id`)
type Struct = $Struct;
@ -104,13 +106,13 @@ macro_rules! setup_input_struct {
}
}
$(
impl $DebugTrait for $Struct {
$zalsa::macro_if! { $generate_debug_impl =>
impl std::fmt::Debug for $Struct {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Self::default_debug_fmt(*self, f)
}
}
)?
}
impl $zalsa::SalsaStructInDb for $Struct {
fn register_dependent_fn(_db: &dyn $zalsa::Database, _index: $zalsa::IngredientIndex) {
@ -163,6 +165,25 @@ macro_rules! setup_input_struct {
}
)*
$zalsa::macro_if! { $is_singleton =>
pub fn try_get<$Db>(db: &$Db) -> Option<Self>
where
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
$Db: ?Sized + salsa::Database,
{
$Configuration::ingredient(db.as_salsa_database()).get_singleton_input()
}
#[track_caller]
pub fn get<$Db>(db: &$Db) -> Self
where
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
$Db: ?Sized + salsa::Database,
{
Self::try_get(db).unwrap()
}
}
/// Default debug formatting for this struct (may be useful if you define your own `Debug` impl)
pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
$zalsa::with_attached_database(|db| {

View file

@ -35,10 +35,8 @@ macro_rules! setup_interned_struct {
// Number of fields
num_fields: $N:literal,
// Control customization: each path below either appears or doesn't.
customized: [
$($DebugTrait:path)?, // std::fmt::Debug
],
// If true, generate a debug impl.
generate_debug_impl: $generate_debug_impl:tt,
// Annoyingly macro-rules hygiene does not extend to items defined in the macro.
// We have the procedural macro generate names for those items that are
@ -99,13 +97,13 @@ macro_rules! setup_interned_struct {
unsafe impl Sync for $Struct<'_> {}
$(
impl $DebugTrait for $Struct<'_> {
$zalsa::macro_if! { $generate_debug_impl =>
impl std::fmt::Debug for $Struct<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Self::default_debug_fmt(*self, f)
}
}
)?
}
impl $zalsa::SalsaStructInDb for $Struct<'_> {
fn register_dependent_fn(_db: &dyn $zalsa::Database, _index: $zalsa::IngredientIndex) {

View file

@ -44,10 +44,8 @@ macro_rules! setup_tracked_struct {
// Number of fields
num_fields: $N:literal,
// Control customization: each path below either appears or doesn't.
customized: [
$($DebugTrait:path)?, // std::fmt::Debug
],
// If true, generate a debug impl.
generate_debug_impl: $generate_debug_impl:tt,
// Annoyingly macro-rules hygiene does not extend to items defined in the macro.
// We have the procedural macro generate names for those items that are
@ -167,13 +165,13 @@ macro_rules! setup_tracked_struct {
unsafe impl Sync for $Struct<'_> {}
$(
impl $DebugTrait for $Struct<'_> {
$zalsa::macro_if! { $generate_debug_impl =>
impl std::fmt::Debug for $Struct<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Self::default_debug_fmt(*self, f)
}
}
)?
}
impl<$db_lt> $Struct<$db_lt> {
pub fn $new_fn<$Db>(db: &$db_lt $Db, $($field_id: $field_ty),*) -> Self

View file

@ -84,8 +84,8 @@ impl Macro {
let field_setter_ids = salsa_struct.field_setter_ids();
let field_options = salsa_struct.field_options();
let field_tys = salsa_struct.field_tys();
let DebugTrait = salsa_struct.customized_debug_trait();
let is_singleton = self.args.singleton.is_some();
let generate_debug_impl = salsa_struct.generate_debug_impl();
let zalsa = self.hygiene.ident("zalsa");
let zalsa_struct = self.hygiene.ident("zalsa_struct");
@ -108,9 +108,8 @@ impl Macro {
field_tys: [#(#field_tys),*],
field_indices: [#(#field_indices),*],
num_fields: #num_fields,
customized: [
#DebugTrait,
],
is_singleton: #is_singleton,
generate_debug_impl: #generate_debug_impl,
unused_names: [
#zalsa,
#zalsa_struct,

View file

@ -85,8 +85,7 @@ impl Macro {
let field_getter_ids = salsa_struct.field_getter_ids();
let field_options = salsa_struct.field_options();
let field_tys = salsa_struct.field_tys();
let DebugTrait = salsa_struct.customized_debug_trait();
let generate_debug_impl = salsa_struct.generate_debug_impl();
let zalsa = self.hygiene.ident("zalsa");
let zalsa_struct = self.hygiene.ident("zalsa_struct");
@ -109,9 +108,7 @@ impl Macro {
field_tys: [#(#field_tys),*],
field_indices: [#(#field_indices),*],
num_fields: #num_fields,
customized: [
#DebugTrait,
],
generate_debug_impl: #generate_debug_impl,
unused_names: [
#zalsa,
#zalsa_struct,

View file

@ -213,12 +213,8 @@ where
.collect()
}
pub fn customized_debug_trait(&self) -> TokenStream {
if self.args.no_debug.is_some() {
quote!()
} else {
quote!(std::fmt::Debug)
}
pub fn generate_debug_impl(&self) -> bool {
self.args.no_debug.is_none()
}
}

View file

@ -81,8 +81,7 @@ impl Macro {
let num_fields = salsa_struct.num_fields();
let field_options = salsa_struct.field_options();
let field_tys = salsa_struct.field_tys();
let DebugTrait = salsa_struct.customized_debug_trait();
let generate_debug_impl = salsa_struct.generate_debug_impl();
let zalsa = self.hygiene.ident("zalsa");
let zalsa_struct = self.hygiene.ident("zalsa_struct");
@ -108,9 +107,7 @@ impl Macro {
id_field_indices: [#(#id_field_indices),*],
field_options: [#(#field_options),*],
num_fields: #num_fields,
customized: [
#DebugTrait,
],
generate_debug_impl: #generate_debug_impl,
unused_names: [
#zalsa,
#zalsa_struct,

View file

@ -26,6 +26,7 @@ use crate::{
pub trait Configuration: Any {
const DEBUG_NAME: &'static str;
const FIELD_DEBUG_NAMES: &'static [&'static str];
const IS_SINGLETON: bool;
/// The input struct (which wraps an `Id`)
type Struct: FromId + 'static + Send + Sync;
@ -94,6 +95,11 @@ impl<C: Configuration> IngredientImpl<C> {
}
pub fn new_input(&self, fields: C::Fields, stamps: C::Stamps) -> C::Struct {
// If declared as a singleton, only allow a single instance
if C::IS_SINGLETON && self.counter.load(Ordering::Relaxed) >= 1 {
panic!("singleton struct may not be duplicated");
}
let next_id = Id::from_u32(self.counter.fetch_add(1, Ordering::Relaxed));
let value = Value {
struct_ingredient_index: self.ingredient_index,
@ -130,19 +136,12 @@ impl<C: Configuration> IngredientImpl<C> {
setter(&mut r.fields)
}
/// Creates a new singleton input.
pub fn new_singleton_input(&self, _runtime: &Runtime) -> C::Struct {
// when one exists already, panic
if self.counter.load(Ordering::Relaxed) >= 1 {
panic!("singleton struct may not be duplicated");
}
// fresh new ingredient
self.counter.store(1, Ordering::Relaxed);
C::Struct::from_id(Id::from_u32(0))
}
/// Get the singleton input previously created.
pub fn get_singleton_input(&self, _runtime: &Runtime) -> Option<C::Struct> {
/// Get the singleton input previously created (if any).
pub fn get_singleton_input(&self) -> Option<C::Struct> {
assert!(
C::IS_SINGLETON,
"get_singleton_input invoked on a non-singleton"
);
(self.counter.load(Ordering::Relaxed) > 0).then(|| C::Struct::from_id(Id::from_u32(0)))
}

View file

@ -90,6 +90,7 @@ pub mod plumbing {
pub use crate::update::helper::Dispatch as UpdateDispatch;
pub use crate::update::helper::Fallback as UpdateFallback;
pub use salsa_macro_rules::macro_if;
pub use salsa_macro_rules::maybe_backdate;
pub use salsa_macro_rules::maybe_clone;
pub use salsa_macro_rules::maybe_cloned_ty;

View file

@ -1,26 +1,20 @@
//! Test that creating a tracked struct outside of a
//! tracked function panics with an assert message.
#[salsa::jar(db = Db)]
struct Jar(MyTracked<'_>);
trait Db: salsa::DbWithJar<Jar> {}
#[salsa::tracked]
struct MyTracked<'db> {
field: u32,
}
#[salsa::db(Jar)]
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
impl Db for Database {}
#[test]
#[should_panic(
expected = "cannot create a tracked struct disambiguator outside of a tracked function"

View file

@ -3,33 +3,32 @@
//! Singleton structs are created only once. Subsequent `get`s and `new`s after creation return the same `Id`.
use expect_test::expect;
use salsa::DebugWithDb;
mod common;
use common::{HasLogger, Logger};
use salsa::Database as _;
use test_log::test;
#[salsa::jar(db = Db)]
struct Jar(MyInput);
trait Db: salsa::DbWithJar<Jar> + HasLogger {}
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input(singleton)]
struct MyInput {
field: u32,
#[id]
id_field: u16,
}
#[salsa::db(Jar)]
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {
@ -65,9 +64,10 @@ fn twice() {
#[test]
fn debug() {
let db = Database::default();
let input = MyInput::new(&db, 3, 4);
let actual = format!("{:?}", input.debug(&db));
let expected = expect!["MyInput { [salsa id]: 0, field: 3, id_field: 4 }"];
expected.assert_eq(&actual);
Database::default().attach(|db| {
let input = MyInput::new(db, 3, 4);
let actual = format!("{:?}", input);
let expected = expect!["MyInput { [salsa id]: 0, field: 3, id_field: 4 }"];
expected.assert_eq(&actual);
});
}

View file

@ -2,28 +2,22 @@
//! 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 {
fn tracked_fn(db: &dyn salsa::Database) -> u32 {
44
}
#[test]
fn execute() {
#[salsa::db(Jar)]
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
impl Db for Database {}
let mut db = Database::default();
assert_eq!(tracked_fn(&db), 44);
}

View file

@ -2,33 +2,27 @@
//! compiles and executes successfully.
#![allow(warnings)]
#[salsa::jar(db = Db)]
struct Jar(MyInput, tracked_fn);
trait Db: salsa::DbWithJar<Jar> {}
#[salsa::input]
struct MyInput {
field: u32,
}
#[salsa::tracked]
fn tracked_fn(db: &dyn Db, input: MyInput) -> u32 {
fn tracked_fn(db: &dyn salsa::Database, input: MyInput) -> u32 {
input.field(db) * 2
}
#[test]
fn execute() {
#[salsa::db(Jar)]
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
impl Db for Database {}
let mut db = Database::default();
let input = MyInput::new(&db, 22);
assert_eq!(tracked_fn(&db, input), 44);

View file

@ -1,11 +1,6 @@
//! Test that a `tracked` fn on a `salsa::input`
//! compiles and executes successfully.
#[salsa::jar(db = Db)]
struct Jar(MyInput, MyTracked<'_>, tracked_fn);
trait Db: salsa::DbWithJar<Jar> {}
#[salsa::input]
struct MyInput {
field: u32,
@ -17,20 +12,19 @@ struct MyTracked<'db> {
}
#[salsa::tracked]
fn tracked_fn<'db>(db: &'db dyn Db, input: MyInput) -> MyTracked<'db> {
fn tracked_fn<'db>(db: &'db dyn salsa::Database, input: MyInput) -> MyTracked<'db> {
MyTracked::new(db, input.field(db) * 2)
}
#[salsa::db(Jar)]
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
impl Db for Database {}
#[test]
fn execute() {
let db = Database::default();

View file

@ -2,11 +2,6 @@
//! compiles and executes successfully.
#![allow(warnings)]
#[salsa::jar(db = Db)]
struct Jar(MyInput, MyTracked<'_>, tracked_fn, tracked_fn_extra);
trait Db: salsa::DbWithJar<Jar> {}
#[salsa::input]
struct MyInput {
field: u32,
@ -18,7 +13,7 @@ struct MyTracked<'db> {
}
#[salsa::tracked]
fn tracked_fn<'db>(db: &'db dyn Db, input: MyInput) -> MyTracked<'db> {
fn tracked_fn<'db>(db: &'db dyn salsa::Database, input: MyInput) -> MyTracked<'db> {
let t = MyTracked::new(db, input.field(db) * 2);
if input.field(db) != 0 {
tracked_fn_extra::specify(db, t, 2222);
@ -26,21 +21,20 @@ fn tracked_fn<'db>(db: &'db dyn Db, input: MyInput) -> MyTracked<'db> {
t
}
#[salsa::tracked(jar = Jar, specify)]
fn tracked_fn_extra<'db>(_db: &'db dyn Db, _input: MyTracked<'db>) -> u32 {
#[salsa::tracked(specify)]
fn tracked_fn_extra<'db>(_db: &'db dyn salsa::Database, _input: MyTracked<'db>) -> u32 {
0
}
#[salsa::db(Jar)]
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
impl Db for Database {}
#[test]
fn execute_when_specified() {
let mut db = Database::default();

View file

@ -4,12 +4,11 @@
use expect_test::expect;
mod common;
use common::{HasLogger, Logger};
use salsa::Setter;
use test_log::test;
#[salsa::jar(db = Db)]
struct Jar(MyInput, MyTracked<'_>, final_result, intermediate_result);
trait Db: salsa::DbWithJar<Jar> + HasLogger {}
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input]
struct MyInput {
@ -35,15 +34,17 @@ fn intermediate_result<'db>(db: &'db dyn Db, input: MyInput) -> MyTracked<'db> {
tracked
}
#[salsa::db(Jar)]
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {

View file

@ -3,10 +3,8 @@ use salsa::{Database as SalsaDatabase, DebugWithDb};
mod common;
use common::{HasLogger, Logger};
#[salsa::jar(db = Db)]
struct Jar(MyInput, MyTracked<'_>, tracked_fn, tracked_fn_extra);
trait Db: salsa::DbWithJar<Jar> + HasLogger {}
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input]
struct MyInput {
@ -20,27 +18,29 @@ struct MyTracked<'db> {
#[salsa::tracked]
fn tracked_fn<'db>(db: &'db dyn Db, input: MyInput) -> u32 {
db.push_log(format!("tracked_fn({:?})", input.debug(db)));
db.push_log(format!("tracked_fn({input:?})"));
let t = MyTracked::new(db, input.field(db) * 2);
tracked_fn_extra::specify(db, t, 2222);
tracked_fn_extra(db, t)
}
#[salsa::tracked(jar = Jar, specify)]
#[salsa::tracked(specify)]
fn tracked_fn_extra<'db>(db: &dyn Db, input: MyTracked<'db>) -> u32 {
db.push_log(format!("tracked_fn_extra({:?})", input.debug(db)));
db.push_log(format!("tracked_fn_extra({input:?})"));
0
}
#[salsa::db(Jar)]
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {