Skip to content

Commit dba66f1

Browse files
authored
Use inventory for static ingredient registration (#934)
* use `inventory` for static ingredient registration * remove unnecessary synchronization from memo tables * use global ingredient caches for database-independent ingredients * add manual ingredient registration API * remove static ingredient index optimization when manual registration is in use * fix atomic imports * simplify ingredient caches
1 parent d28d66b commit dba66f1

File tree

130 files changed

+1020
-610
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

130 files changed

+1020
-610
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ jobs:
5555
run: cargo clippy --workspace --all-targets -- -D warnings
5656
- name: Test
5757
run: cargo nextest run --workspace --all-targets --no-fail-fast
58+
- name: Test Manual Registration
59+
run: cargo nextest run --workspace --tests --no-fail-fast --no-default-features --features macros
5860
- name: Test docs
5961
run: cargo test --workspace --doc
6062
- name: Check (without default features)

Cargo.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ hashbrown = "0.15"
1919
hashlink = "0.10"
2020
indexmap = "2"
2121
intrusive-collections = "0.9.7"
22-
papaya = "0.2.3"
2322
parking_lot = "0.12"
2423
portable-atomic = "1"
2524
rustc-hash = "2"
2625
smallvec = "1"
2726
tracing = { version = "0.1", default-features = false, features = ["std"] }
2827

28+
# Automatic ingredient registration.
29+
inventory = { version = "0.3.20", optional = true }
30+
2931
# parallel map
3032
rayon = { version = "1.10.0", optional = true }
3133

@@ -36,7 +38,8 @@ thin-vec = "0.2.13"
3638
shuttle = { version = "0.8.0", optional = true }
3739

3840
[features]
39-
default = ["salsa_unstable", "rayon", "macros"]
41+
default = ["salsa_unstable", "rayon", "macros", "inventory"]
42+
inventory = ["dep:inventory"]
4043
shuttle = ["dep:shuttle"]
4144
# FIXME: remove `salsa_unstable` before 1.0.
4245
salsa_unstable = []

components/salsa-macro-rules/src/setup_accumulator_impl.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,21 @@ macro_rules! setup_accumulator_impl {
2121
use salsa::plumbing as $zalsa;
2222
use salsa::plumbing::accumulator as $zalsa_struct;
2323

24+
impl $zalsa::HasJar for $Struct {
25+
type Jar = $zalsa_struct::JarImpl<$Struct>;
26+
const KIND: $zalsa::JarKind = $zalsa::JarKind::Struct;
27+
}
28+
29+
$zalsa::register_jar! {
30+
$zalsa::ErasedJar::erase::<$Struct>()
31+
}
32+
2433
fn $ingredient(zalsa: &$zalsa::Zalsa) -> &$zalsa_struct::IngredientImpl<$Struct> {
2534
static $CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Struct>> =
2635
$zalsa::IngredientCache::new();
2736

2837
$CACHE.get_or_create(zalsa, || {
29-
zalsa
30-
.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Struct>>()
31-
.get_or_create()
38+
zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Struct>>()
3239
})
3340
}
3441

components/salsa-macro-rules/src/setup_input_struct.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ macro_rules! setup_input_struct {
7474

7575
type $Configuration = $Struct;
7676

77+
impl $zalsa::HasJar for $Struct {
78+
type Jar = $zalsa_struct::JarImpl<$Configuration>;
79+
const KIND: $zalsa::JarKind = $zalsa::JarKind::Struct;
80+
}
81+
82+
$zalsa::register_jar! {
83+
$zalsa::ErasedJar::erase::<$Struct>()
84+
}
85+
7786
impl $zalsa_struct::Configuration for $Configuration {
7887
const LOCATION: $zalsa::Location = $zalsa::Location {
7988
file: file!(),
@@ -101,14 +110,14 @@ macro_rules! setup_input_struct {
101110
$zalsa::IngredientCache::new();
102111

103112
CACHE.get_or_create(zalsa, || {
104-
zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().get_or_create()
113+
zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>()
105114
})
106115
}
107116

108117
pub fn ingredient_mut(db: &mut dyn $zalsa::Database) -> (&mut $zalsa_struct::IngredientImpl<Self>, &mut $zalsa::Runtime) {
109118
let zalsa_mut = db.zalsa_mut();
110119
zalsa_mut.new_revision();
111-
let index = zalsa_mut.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().get_or_create();
120+
let index = zalsa_mut.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>();
112121
let (ingredient, runtime) = zalsa_mut.lookup_ingredient_mut(index);
113122
let ingredient = ingredient.assert_type_mut::<$zalsa_struct::IngredientImpl<Self>>();
114123
(ingredient, runtime)
@@ -149,8 +158,8 @@ macro_rules! setup_input_struct {
149158
impl $zalsa::SalsaStructInDb for $Struct {
150159
type MemoIngredientMap = $zalsa::MemoIngredientSingletonIndex;
151160

152-
fn lookup_or_create_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
153-
aux.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().get_or_create().into()
161+
fn lookup_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
162+
aux.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().into()
154163
}
155164

156165
#[inline]

components/salsa-macro-rules/src/setup_interned_struct.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,15 @@ macro_rules! setup_interned_struct {
9292

9393
type $Configuration = $StructWithStatic;
9494

95+
impl<$($db_lt_arg)?> $zalsa::HasJar for $Struct<$($db_lt_arg)?> {
96+
type Jar = $zalsa_struct::JarImpl<$Configuration>;
97+
const KIND: $zalsa::JarKind = $zalsa::JarKind::Struct;
98+
}
99+
100+
$zalsa::register_jar! {
101+
$zalsa::ErasedJar::erase::<$StructWithStatic>()
102+
}
103+
95104
type $StructDataIdent<$db_lt> = ($($field_ty,)*);
96105

97106
/// Key to use during hash lookups. Each field is some type that implements `Lookup<T>`
@@ -149,7 +158,7 @@ macro_rules! setup_interned_struct {
149158

150159
let zalsa = db.zalsa();
151160
CACHE.get_or_create(zalsa, || {
152-
zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().get_or_create()
161+
zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>()
153162
})
154163
}
155164
}
@@ -181,8 +190,8 @@ macro_rules! setup_interned_struct {
181190
impl< $($db_lt_arg)? > $zalsa::SalsaStructInDb for $Struct< $($db_lt_arg)? > {
182191
type MemoIngredientMap = $zalsa::MemoIngredientSingletonIndex;
183192

184-
fn lookup_or_create_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
185-
aux.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().get_or_create().into()
193+
fn lookup_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
194+
aux.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().into()
186195
}
187196

188197
#[inline]

components/salsa-macro-rules/src/setup_tracked_fn.rs

Lines changed: 29 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,16 @@ macro_rules! setup_tracked_fn {
9191

9292
struct $Configuration;
9393

94+
$zalsa::register_jar! {
95+
$zalsa::ErasedJar::erase::<$fn_name>()
96+
}
97+
98+
#[allow(non_local_definitions)]
99+
impl $zalsa::HasJar for $fn_name {
100+
type Jar = $fn_name;
101+
const KIND: $zalsa::JarKind = $zalsa::JarKind::TrackedFn;
102+
}
103+
94104
static $FN_CACHE: $zalsa::IngredientCache<$zalsa::function::IngredientImpl<$Configuration>> =
95105
$zalsa::IngredientCache::new();
96106

@@ -108,7 +118,7 @@ macro_rules! setup_tracked_fn {
108118
impl $zalsa::SalsaStructInDb for $InternedData<'_> {
109119
type MemoIngredientMap = $zalsa::MemoIngredientSingletonIndex;
110120

111-
fn lookup_or_create_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
121+
fn lookup_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
112122
$zalsa::IngredientIndices::empty()
113123
}
114124

@@ -155,27 +165,19 @@ macro_rules! setup_tracked_fn {
155165
impl $Configuration {
156166
fn fn_ingredient(db: &dyn $Db) -> &$zalsa::function::IngredientImpl<$Configuration> {
157167
let zalsa = db.zalsa();
158-
$FN_CACHE.get_or_create(zalsa, || {
159-
let jar_entry = zalsa.lookup_jar_by_type::<$Configuration>();
160-
161-
// If the ingredient has already been inserted, we know that the downcaster
162-
// has also been registered. This is a fast-path for multi-database use cases
163-
// that bypass the ingredient cache and will always execute this closure.
164-
if let Some(index) = jar_entry.get() {
165-
return index;
166-
}
167-
168-
<dyn $Db as $Db>::zalsa_register_downcaster(db);
169-
jar_entry.get_or_create()
170-
})
168+
$FN_CACHE
169+
.get_or_create(zalsa, || zalsa.lookup_jar_by_type::<$fn_name>())
170+
.get_or_init(|| <dyn $Db as $Db>::zalsa_register_downcaster(db))
171171
}
172172

173173
pub fn fn_ingredient_mut(db: &mut dyn $Db) -> &mut $zalsa::function::IngredientImpl<Self> {
174-
<dyn $Db as $Db>::zalsa_register_downcaster(db);
174+
let view = <dyn $Db as $Db>::zalsa_register_downcaster(db);
175175
let zalsa_mut = db.zalsa_mut();
176-
let index = zalsa_mut.lookup_jar_by_type::<$Configuration>().get_or_create();
176+
let index = zalsa_mut.lookup_jar_by_type::<$fn_name>();
177177
let (ingredient, _) = zalsa_mut.lookup_ingredient_mut(index);
178-
ingredient.assert_type_mut::<$zalsa::function::IngredientImpl<Self>>()
178+
let ingredient = ingredient.assert_type_mut::<$zalsa::function::IngredientImpl<Self>>();
179+
ingredient.get_or_init(|| view);
180+
ingredient
179181
}
180182

181183
$zalsa::macro_if! { $needs_interner =>
@@ -184,8 +186,7 @@ macro_rules! setup_tracked_fn {
184186
) -> &$zalsa::interned::IngredientImpl<$Configuration> {
185187
let zalsa = db.zalsa();
186188
$INTERN_CACHE.get_or_create(zalsa, || {
187-
<dyn $Db as $Db>::zalsa_register_downcaster(db);
188-
zalsa.lookup_jar_by_type::<$Configuration>().get_or_create().successor(0)
189+
zalsa.lookup_jar_by_type::<$fn_name>().successor(0)
189190
})
190191
}
191192
}
@@ -248,42 +249,31 @@ macro_rules! setup_tracked_fn {
248249
}
249250
}
250251

251-
impl $zalsa::Jar for $Configuration {
252-
fn create_dependencies(zalsa: &$zalsa::Zalsa) -> $zalsa::IngredientIndices
253-
where
254-
Self: Sized
255-
{
256-
$zalsa::macro_if! {
257-
if $needs_interner {
258-
$zalsa::IngredientIndices::empty()
259-
} else {
260-
<$InternedData as $zalsa::SalsaStructInDb>::lookup_or_create_ingredient_index(zalsa)
261-
}
262-
}
263-
}
264-
252+
#[allow(non_local_definitions)]
253+
impl $zalsa::Jar for $fn_name {
265254
fn create_ingredients(
266-
zalsa: &$zalsa::Zalsa,
255+
zalsa: &mut $zalsa::Zalsa,
267256
first_index: $zalsa::IngredientIndex,
268-
struct_index: $zalsa::IngredientIndices,
269257
) -> Vec<Box<dyn $zalsa::Ingredient>> {
270258
let struct_index: $zalsa::IngredientIndices = $zalsa::macro_if! {
271259
if $needs_interner {
272260
first_index.successor(0).into()
273261
} else {
274-
struct_index
262+
// Note that struct ingredients are created before tracked functions,
263+
// so this cannot panic.
264+
<$InternedData as $zalsa::SalsaStructInDb>::lookup_ingredient_index(zalsa)
275265
}
276266
};
277267

278268
$zalsa::macro_if! { $needs_interner =>
279-
let intern_ingredient = <$zalsa::interned::IngredientImpl<$Configuration>>::new(
269+
let mut intern_ingredient = <$zalsa::interned::IngredientImpl<$Configuration>>::new(
280270
first_index.successor(0)
281271
);
282272
}
283273

284274
let intern_ingredient_memo_types = $zalsa::macro_if! {
285275
if $needs_interner {
286-
Some($zalsa::Ingredient::memo_table_types(&intern_ingredient))
276+
Some($zalsa::Ingredient::memo_table_types_mut(&mut intern_ingredient))
287277
} else {
288278
None
289279
}
@@ -303,7 +293,6 @@ macro_rules! setup_tracked_fn {
303293
first_index,
304294
memo_ingredient_indices,
305295
$lru,
306-
zalsa.views().downcaster_for::<dyn $Db>(),
307296
);
308297
$zalsa::macro_if! {
309298
if $needs_interner {
@@ -386,6 +375,7 @@ macro_rules! setup_tracked_fn {
386375
$zalsa::return_mode_expression!(($return_mode, __, __), $output_ty, result,)
387376
})
388377
}
378+
389379
// The struct needs be last in the macro expansion in order to make the tracked
390380
// function's ident be identified as a function, not a struct, during semantic highlighting.
391381
// for more details, see https://github.com/salsa-rs/salsa/pull/612.

components/salsa-macro-rules/src/setup_tracked_struct.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,24 @@ macro_rules! setup_tracked_struct {
107107
std::marker::PhantomData<fn() -> &$db_lt ()>
108108
);
109109

110-
#[allow(clippy::all)]
111110
#[allow(dead_code)]
111+
#[allow(clippy::all)]
112112
const _: () = {
113113
use salsa::plumbing as $zalsa;
114114
use $zalsa::tracked_struct as $zalsa_struct;
115115
use $zalsa::Revision as $Revision;
116116

117117
type $Configuration = $Struct<'static>;
118118

119+
impl<$db_lt> $zalsa::HasJar for $Struct<$db_lt> {
120+
type Jar = $zalsa_struct::JarImpl<$Configuration>;
121+
const KIND: $zalsa::JarKind = $zalsa::JarKind::Struct;
122+
}
123+
124+
$zalsa::register_jar! {
125+
$zalsa::ErasedJar::erase::<$Struct<'static>>()
126+
}
127+
119128
impl $zalsa_struct::Configuration for $Configuration {
120129
const LOCATION: $zalsa::Location = $zalsa::Location {
121130
file: file!(),
@@ -188,7 +197,7 @@ macro_rules! setup_tracked_struct {
188197
$zalsa::IngredientCache::new();
189198

190199
CACHE.get_or_create(zalsa, || {
191-
zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().get_or_create()
200+
zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>()
192201
})
193202
}
194203
}
@@ -210,8 +219,8 @@ macro_rules! setup_tracked_struct {
210219
impl $zalsa::SalsaStructInDb for $Struct<'_> {
211220
type MemoIngredientMap = $zalsa::MemoIngredientSingletonIndex;
212221

213-
fn lookup_or_create_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
214-
aux.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().get_or_create().into()
222+
fn lookup_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
223+
aux.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().into()
215224
}
216225

217226
#[inline]

components/salsa-macros/src/db.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ impl DbMacro {
110110
let trait_name = &input.ident;
111111
input.items.push(parse_quote! {
112112
#[doc(hidden)]
113-
fn zalsa_register_downcaster(&self);
113+
fn zalsa_register_downcaster(&self) -> salsa::plumbing::DatabaseDownCaster<dyn #trait_name>;
114114
});
115115

116116
let comment = format!(" Downcast a [`dyn Database`] to a [`dyn {trait_name}`]");
@@ -135,10 +135,11 @@ impl DbMacro {
135135
};
136136

137137
input.items.push(parse_quote! {
138+
#[cold]
139+
#[inline(never)]
138140
#[doc(hidden)]
139-
#[inline(always)]
140-
fn zalsa_register_downcaster(&self) {
141-
salsa::plumbing::views(self).add(<Self as #TraitPath>::downcast);
141+
fn zalsa_register_downcaster(&self) -> salsa::plumbing::DatabaseDownCaster<dyn #TraitPath> {
142+
salsa::plumbing::views(self).add(<Self as #TraitPath>::downcast)
142143
}
143144
});
144145
input.items.push(parse_quote! {

components/salsa-macros/src/fn_util.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub fn input_ids(hygiene: &Hygiene, sig: &syn::Signature, skip: usize) -> Vec<sy
1515
}
1616
}
1717

18-
hygiene.ident(&format!("input{index}"))
18+
hygiene.ident(format!("input{index}"))
1919
})
2020
.collect()
2121
}

components/salsa-macros/src/hygiene.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,23 @@ impl Hygiene {
5050
/// Generates an identifier similar to `text` but
5151
/// distinct from any identifiers that appear in the user's
5252
/// code.
53-
pub(crate) fn ident(&self, text: &str) -> syn::Ident {
53+
pub(crate) fn ident(&self, text: impl AsRef<str>) -> syn::Ident {
5454
// Make the default be `foo_` rather than `foo` -- this helps detect
5555
// cases where people wrote `foo` instead of `#foo` or `$foo` in the generated code.
56-
let mut buffer = format!("{text}_");
56+
let mut buffer = format!("{}_", text.as_ref());
5757

5858
while self.user_tokens.contains(&buffer) {
5959
buffer.push('_');
6060
}
6161

6262
syn::Ident::new(&buffer, proc_macro2::Span::call_site())
6363
}
64+
65+
/// Generates an identifier similar to `text` but distinct from any identifiers
66+
/// that appear in the user's code.
67+
///
68+
/// The identifier must be unique relative to the `scope` identifier.
69+
pub(crate) fn scoped_ident(&self, scope: &syn::Ident, text: &str) -> syn::Ident {
70+
self.ident(format!("{scope}_{text}"))
71+
}
6472
}

0 commit comments

Comments
 (0)