Skip to content

Commit 211bc15

Browse files
authored
Do manual trait casting (#922)
* Do manual trait upcasting instead of downcasting * Remove another dynamic `zalsa` call * Rename UpCaster back to DownCaster * Address reviews
1 parent f3dc2f3 commit 211bc15

28 files changed

+371
-276
lines changed

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,7 @@ macro_rules! setup_input_struct {
118118
}
119119
}
120120

121-
pub fn ingredient_mut(db: &mut dyn $zalsa::Database) -> (&mut $zalsa_struct::IngredientImpl<Self>, &mut $zalsa::Runtime) {
122-
let zalsa_mut = db.zalsa_mut();
121+
pub fn ingredient_mut(zalsa_mut: &mut $zalsa::Zalsa) -> (&mut $zalsa_struct::IngredientImpl<Self>, &mut $zalsa::Runtime) {
123122
zalsa_mut.new_revision();
124123
let index = zalsa_mut.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>();
125124
let (ingredient, runtime) = zalsa_mut.lookup_ingredient_mut(index);
@@ -208,8 +207,10 @@ macro_rules! setup_input_struct {
208207
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
209208
$Db: ?Sized + $zalsa::Database,
210209
{
211-
let fields = $Configuration::ingredient_(db.zalsa()).field(
212-
db.as_dyn_database(),
210+
let (zalsa, zalsa_local) = db.zalsas();
211+
let fields = $Configuration::ingredient_(zalsa).field(
212+
zalsa,
213+
zalsa_local,
213214
self,
214215
$field_index,
215216
);
@@ -228,7 +229,8 @@ macro_rules! setup_input_struct {
228229
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
229230
$Db: ?Sized + $zalsa::Database,
230231
{
231-
let (ingredient, revision) = $Configuration::ingredient_mut(db.as_dyn_database_mut());
232+
let zalsa = db.zalsa_mut();
233+
let (ingredient, revision) = $Configuration::ingredient_mut(zalsa);
232234
$zalsa::input::SetterImpl::new(
233235
revision,
234236
self,
@@ -267,7 +269,8 @@ macro_rules! setup_input_struct {
267269
$(for<'__trivial_bounds> $field_ty: std::fmt::Debug),*
268270
{
269271
$zalsa::with_attached_database(|db| {
270-
let fields = $Configuration::ingredient(db).leak_fields(db, this);
272+
let zalsa = db.zalsa();
273+
let fields = $Configuration::ingredient_(zalsa).leak_fields(zalsa, this);
271274
let mut f = f.debug_struct(stringify!($Struct));
272275
let f = f.field("[salsa id]", &$zalsa::AsId::as_id(&this));
273276
$(
@@ -296,11 +299,11 @@ macro_rules! setup_input_struct {
296299
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
297300
$Db: ?Sized + salsa::Database
298301
{
299-
let zalsa = db.zalsa();
302+
let (zalsa, zalsa_local) = db.zalsas();
300303
let current_revision = zalsa.current_revision();
301304
let ingredient = $Configuration::ingredient_(zalsa);
302305
let (fields, revision, durabilities) = builder::builder_into_inner(self, current_revision);
303-
ingredient.new_input(db.as_dyn_database(), fields, revision, durabilities)
306+
ingredient.new_input(zalsa, zalsa_local, fields, revision, durabilities)
304307
}
305308
}
306309

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

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -149,15 +149,11 @@ macro_rules! setup_interned_struct {
149149
}
150150

151151
impl $Configuration {
152-
pub fn ingredient<Db>(db: &Db) -> &$zalsa_struct::IngredientImpl<Self>
153-
where
154-
Db: ?Sized + $zalsa::Database,
152+
pub fn ingredient(zalsa: &$zalsa::Zalsa) -> &$zalsa_struct::IngredientImpl<Self>
155153
{
156154
static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
157155
$zalsa::IngredientCache::new();
158156

159-
let zalsa = db.zalsa();
160-
161157
// SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the only
162158
// ingredient created by our jar is the struct ingredient.
163159
unsafe {
@@ -239,7 +235,8 @@ macro_rules! setup_interned_struct {
239235
$field_ty: $zalsa::interned::HashEqLike<$indexed_ty>,
240236
)*
241237
{
242-
$Configuration::ingredient(db).intern(db.as_dyn_database(),
238+
let (zalsa, zalsa_local) = db.zalsas();
239+
$Configuration::ingredient(zalsa).intern(zalsa, zalsa_local,
243240
StructKey::<$db_lt>($($field_id,)* std::marker::PhantomData::default()), |_, data| ($($zalsa::interned::Lookup::into_owned(data.$field_index),)*))
244241
}
245242

@@ -250,7 +247,8 @@ macro_rules! setup_interned_struct {
250247
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
251248
$Db: ?Sized + $zalsa::Database,
252249
{
253-
let fields = $Configuration::ingredient(db).fields(db.as_dyn_database(), self);
250+
let zalsa = db.zalsa();
251+
let fields = $Configuration::ingredient(zalsa).fields(zalsa, self);
254252
$zalsa::return_mode_expression!(
255253
$field_option,
256254
$field_ty,
@@ -262,7 +260,8 @@ macro_rules! setup_interned_struct {
262260
/// Default debug formatting for this struct (may be useful if you define your own `Debug` impl)
263261
pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
264262
$zalsa::with_attached_database(|db| {
265-
let fields = $Configuration::ingredient(db).fields(db.as_dyn_database(), this);
263+
let zalsa = db.zalsa();
264+
let fields = $Configuration::ingredient(zalsa).fields(zalsa, this);
266265
let mut f = f.debug_struct(stringify!($Struct));
267266
$(
268267
let f = f.field(stringify!($field_id), &fields.$field_index);

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

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -175,17 +175,21 @@ macro_rules! setup_tracked_fn {
175175
impl $Configuration {
176176
fn fn_ingredient(db: &dyn $Db) -> &$zalsa::function::IngredientImpl<$Configuration> {
177177
let zalsa = db.zalsa();
178+
Self::fn_ingredient_(db, zalsa)
179+
}
178180

181+
#[inline]
182+
fn fn_ingredient_<'z>(db: &dyn $Db, zalsa: &'z $zalsa::Zalsa) -> &'z $zalsa::function::IngredientImpl<$Configuration> {
179183
// SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the first
180184
// ingredient created by our jar is the function ingredient.
181185
unsafe {
182186
$FN_CACHE.get_or_create(zalsa, || zalsa.lookup_jar_by_type::<$fn_name>())
183187
}
184-
.get_or_init(|| <dyn $Db as $Db>::zalsa_register_downcaster(db))
188+
.get_or_init(|| *<dyn $Db as $Db>::zalsa_register_downcaster(db))
185189
}
186190

187191
pub fn fn_ingredient_mut(db: &mut dyn $Db) -> &mut $zalsa::function::IngredientImpl<Self> {
188-
let view = <dyn $Db as $Db>::zalsa_register_downcaster(db);
192+
let view = *<dyn $Db as $Db>::zalsa_register_downcaster(db);
189193
let zalsa_mut = db.zalsa_mut();
190194
let index = zalsa_mut.lookup_jar_by_type::<$fn_name>();
191195
let (ingredient, _) = zalsa_mut.lookup_ingredient_mut(index);
@@ -199,7 +203,12 @@ macro_rules! setup_tracked_fn {
199203
db: &dyn $Db,
200204
) -> &$zalsa::interned::IngredientImpl<$Configuration> {
201205
let zalsa = db.zalsa();
202-
206+
Self::intern_ingredient_(zalsa)
207+
}
208+
#[inline]
209+
fn intern_ingredient_<'z>(
210+
zalsa: &'z $zalsa::Zalsa
211+
) -> &'z $zalsa::interned::IngredientImpl<$Configuration> {
203212
// SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the second
204213
// ingredient created by our jar is the interned ingredient (given `needs_interner`).
205214
unsafe {
@@ -257,12 +266,12 @@ macro_rules! setup_tracked_fn {
257266
$($cycle_recovery_fn)*(db, value, count, $($input_id),*)
258267
}
259268

260-
fn id_to_input<$db_lt>(db: &$db_lt Self::DbView, key: salsa::Id) -> Self::Input<$db_lt> {
269+
fn id_to_input<$db_lt>(zalsa: &$db_lt $zalsa::Zalsa, key: salsa::Id) -> Self::Input<$db_lt> {
261270
$zalsa::macro_if! {
262271
if $needs_interner {
263-
$Configuration::intern_ingredient(db).data(db.as_dyn_database(), key).clone()
272+
$Configuration::intern_ingredient_(zalsa).data(zalsa, key).clone()
264273
} else {
265-
$zalsa::FromIdWithDb::from_id(key, db.zalsa())
274+
$zalsa::FromIdWithDb::from_id(key, zalsa)
266275
}
267276
}
268277
}
@@ -340,9 +349,10 @@ macro_rules! setup_tracked_fn {
340349
) -> Vec<&$db_lt A> {
341350
use salsa::plumbing as $zalsa;
342351
let key = $zalsa::macro_if! {
343-
if $needs_interner {
344-
$Configuration::intern_ingredient($db).intern_id($db.as_dyn_database(), ($($input_id),*), |_, data| data)
345-
} else {
352+
if $needs_interner {{
353+
let (zalsa, zalsa_local) = $db.zalsas();
354+
$Configuration::intern_ingredient($db).intern_id(zalsa, zalsa_local, ($($input_id),*), |_, data| data)
355+
}} else {
346356
$zalsa::AsId::as_id(&($($input_id),*))
347357
}
348358
};
@@ -380,14 +390,17 @@ macro_rules! setup_tracked_fn {
380390
}
381391

382392
$zalsa::attach($db, || {
393+
let (zalsa, zalsa_local) = $db.zalsas();
383394
let result = $zalsa::macro_if! {
384395
if $needs_interner {
385396
{
386-
let key = $Configuration::intern_ingredient($db).intern_id($db.as_dyn_database(), ($($input_id),*), |_, data| data);
387-
$Configuration::fn_ingredient($db).fetch($db, key)
397+
let key = $Configuration::intern_ingredient_(zalsa).intern_id(zalsa, zalsa_local, ($($input_id),*), |_, data| data);
398+
$Configuration::fn_ingredient_($db, zalsa).fetch($db, zalsa, zalsa_local, key)
388399
}
389400
} else {
390-
$Configuration::fn_ingredient($db).fetch($db, $zalsa::AsId::as_id(&($($input_id),*)))
401+
{
402+
$Configuration::fn_ingredient_($db, zalsa).fetch($db, zalsa, zalsa_local, $zalsa::AsId::as_id(&($($input_id),*)))
403+
}
391404
}
392405
};
393406

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,9 @@ macro_rules! setup_tracked_struct {
282282
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
283283
$Db: ?Sized + $zalsa::Database,
284284
{
285-
$Configuration::ingredient(db.as_dyn_database()).new_struct(
286-
db.as_dyn_database(),
285+
let (zalsa, zalsa_local) = db.zalsas();
286+
$Configuration::ingredient_(zalsa).new_struct(
287+
zalsa,zalsa_local,
287288
($($field_id,)*)
288289
)
289290
}
@@ -295,8 +296,8 @@ macro_rules! setup_tracked_struct {
295296
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
296297
$Db: ?Sized + $zalsa::Database,
297298
{
298-
let db = db.as_dyn_database();
299-
let fields = $Configuration::ingredient(db).tracked_field(db, self, $relative_tracked_index);
299+
let (zalsa, zalsa_local) = db.zalsas();
300+
let fields = $Configuration::ingredient_(zalsa).tracked_field(zalsa, zalsa_local, self, $relative_tracked_index);
300301
$crate::return_mode_expression!(
301302
$tracked_option,
302303
$tracked_ty,
@@ -312,8 +313,8 @@ macro_rules! setup_tracked_struct {
312313
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
313314
$Db: ?Sized + $zalsa::Database,
314315
{
315-
let db = db.as_dyn_database();
316-
let fields = $Configuration::ingredient(db).untracked_field(db, self);
316+
let zalsa = db.zalsa();
317+
let fields = $Configuration::ingredient_(zalsa).untracked_field(zalsa, self);
317318
$crate::return_mode_expression!(
318319
$untracked_option,
319320
$untracked_ty,
@@ -335,7 +336,8 @@ macro_rules! setup_tracked_struct {
335336
$(for<$db_lt> $field_ty: std::fmt::Debug),*
336337
{
337338
$zalsa::with_attached_database(|db| {
338-
let fields = $Configuration::ingredient(db).leak_fields(db, this);
339+
let zalsa = db.zalsa();
340+
let fields = $Configuration::ingredient_(zalsa).leak_fields(zalsa, this);
339341
let mut f = f.debug_struct(stringify!($Struct));
340342
let f = f.field("[salsa id]", &$zalsa::AsId::as_id(&this));
341343
$(

components/salsa-macros/src/db.rs

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

116-
let comment = format!(" Downcast a [`dyn Database`] to a [`dyn {trait_name}`]");
116+
let comment = format!(" downcast `Self` to a [`dyn {trait_name}`]");
117117
input.items.push(parse_quote! {
118118
#[doc = #comment]
119-
///
120-
/// # Safety
121-
///
122-
/// The input database must be of type `Self`.
123119
#[doc(hidden)]
124-
unsafe fn downcast(db: &dyn salsa::plumbing::Database) -> &dyn #trait_name where Self: Sized;
120+
fn downcast(&self) -> &dyn #trait_name where Self: Sized;
125121
});
126122
Ok(())
127123
}
@@ -138,17 +134,17 @@ impl DbMacro {
138134
#[cold]
139135
#[inline(never)]
140136
#[doc(hidden)]
141-
fn zalsa_register_downcaster(&self) -> salsa::plumbing::DatabaseDownCaster<dyn #TraitPath> {
142-
salsa::plumbing::views(self).add(<Self as #TraitPath>::downcast)
137+
fn zalsa_register_downcaster(&self) -> &salsa::plumbing::DatabaseDownCaster<dyn #TraitPath> {
138+
salsa::plumbing::views(self).add::<Self, dyn #TraitPath>(unsafe {
139+
::std::mem::transmute(<Self as #TraitPath>::downcast as fn(_) -> _)
140+
})
143141
}
144142
});
145143
input.items.push(parse_quote! {
146144
#[doc(hidden)]
147145
#[inline(always)]
148-
unsafe fn downcast(db: &dyn salsa::plumbing::Database) -> &dyn #TraitPath where Self: Sized {
149-
debug_assert_eq!(db.type_id(), ::core::any::TypeId::of::<Self>());
150-
// SAFETY: The input database must be of type `Self`.
151-
unsafe { &*salsa::plumbing::transmute_data_ptr::<dyn salsa::plumbing::Database, Self>(db) }
146+
fn downcast(&self) -> &dyn #TraitPath where Self: Sized {
147+
self
152148
}
153149
});
154150
Ok(())

src/accumulator.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ impl<A: Accumulator> Ingredient for IngredientImpl<A> {
102102

103103
unsafe fn maybe_changed_after(
104104
&self,
105-
_db: &dyn Database,
105+
_zalsa: &crate::zalsa::Zalsa,
106+
_db: crate::database::RawDatabase<'_>,
106107
_input: Id,
107108
_revision: Revision,
108109
_cycle_heads: &mut CycleHeads,

src/database.rs

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,39 @@
1-
use std::any::Any;
21
use std::borrow::Cow;
2+
use std::ptr::NonNull;
33

44
use crate::views::DatabaseDownCaster;
55
use crate::zalsa::{IngredientIndex, ZalsaDatabase};
66
use crate::{Durability, Revision};
77

8+
#[derive(Copy, Clone)]
9+
pub struct RawDatabase<'db> {
10+
pub(crate) ptr: NonNull<()>,
11+
_marker: std::marker::PhantomData<&'db dyn Database>,
12+
}
13+
14+
impl<'db, Db: Database + ?Sized> From<&'db Db> for RawDatabase<'db> {
15+
#[inline]
16+
fn from(db: &'db Db) -> Self {
17+
RawDatabase {
18+
ptr: NonNull::from(db).cast(),
19+
_marker: std::marker::PhantomData,
20+
}
21+
}
22+
}
23+
24+
impl<'db, Db: Database + ?Sized> From<&'db mut Db> for RawDatabase<'db> {
25+
#[inline]
26+
fn from(db: &'db mut Db) -> Self {
27+
RawDatabase {
28+
ptr: NonNull::from(db).cast(),
29+
_marker: std::marker::PhantomData,
30+
}
31+
}
32+
}
33+
834
/// The trait implemented by all Salsa databases.
935
/// You can create your own subtraits of this trait using the `#[salsa::db]`(`crate::db`) procedural macro.
10-
pub trait Database: Send + AsDynDatabase + Any + ZalsaDatabase {
36+
pub trait Database: Send + ZalsaDatabase + AsDynDatabase {
1137
/// Enforces current LRU limits, evicting entries if necessary.
1238
///
1339
/// **WARNING:** Just like an ordinary write, this method triggers
@@ -84,59 +110,40 @@ pub trait Database: Send + AsDynDatabase + Any + ZalsaDatabase {
84110
#[cold]
85111
#[inline(never)]
86112
#[doc(hidden)]
87-
fn zalsa_register_downcaster(&self) -> DatabaseDownCaster<dyn Database> {
113+
fn zalsa_register_downcaster(&self) -> &DatabaseDownCaster<dyn Database> {
88114
self.zalsa().views().downcaster_for::<dyn Database>()
89115
// The no-op downcaster is special cased in view caster construction.
90116
}
91117

92118
#[doc(hidden)]
93119
#[inline(always)]
94-
unsafe fn downcast(db: &dyn Database) -> &dyn Database
120+
fn downcast(&self) -> &dyn Database
95121
where
96122
Self: Sized,
97123
{
98124
// No-op
99-
db
125+
self
100126
}
101127
}
102128

103129
/// Upcast to a `dyn Database`.
104130
///
105-
/// Only required because upcasts not yet stabilized (*grr*).
131+
/// Only required because upcasting does not work for unsized generic parameters.
106132
pub trait AsDynDatabase {
107133
fn as_dyn_database(&self) -> &dyn Database;
108-
fn as_dyn_database_mut(&mut self) -> &mut dyn Database;
109134
}
110135

111136
impl<T: Database> AsDynDatabase for T {
112137
#[inline(always)]
113138
fn as_dyn_database(&self) -> &dyn Database {
114139
self
115140
}
116-
117-
#[inline(always)]
118-
fn as_dyn_database_mut(&mut self) -> &mut dyn Database {
119-
self
120-
}
121141
}
122142

123143
pub fn current_revision<Db: ?Sized + Database>(db: &Db) -> Revision {
124144
db.zalsa().current_revision()
125145
}
126146

127-
impl dyn Database {
128-
/// Upcasts `self` to the given view.
129-
///
130-
/// # Panics
131-
///
132-
/// If the view has not been added to the database (see [`crate::views::Views`]).
133-
#[track_caller]
134-
pub fn as_view<DbView: ?Sized + Database>(&self) -> &DbView {
135-
let views = self.zalsa().views();
136-
views.downcaster_for().downcast(self)
137-
}
138-
}
139-
140147
#[cfg(feature = "salsa_unstable")]
141148
pub use memory_usage::IngredientInfo;
142149

0 commit comments

Comments
 (0)