mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-08-09 13:28:19 +00:00
Do manual trait casting (#922)
* Do manual trait upcasting instead of downcasting * Remove another dynamic `zalsa` call * Rename UpCaster back to DownCaster * Address reviews
This commit is contained in:
parent
f3dc2f30f9
commit
211bc158df
28 changed files with 373 additions and 278 deletions
|
@ -118,8 +118,7 @@ macro_rules! setup_input_struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ingredient_mut(db: &mut dyn $zalsa::Database) -> (&mut $zalsa_struct::IngredientImpl<Self>, &mut $zalsa::Runtime) {
|
pub fn ingredient_mut(zalsa_mut: &mut $zalsa::Zalsa) -> (&mut $zalsa_struct::IngredientImpl<Self>, &mut $zalsa::Runtime) {
|
||||||
let zalsa_mut = db.zalsa_mut();
|
|
||||||
zalsa_mut.new_revision();
|
zalsa_mut.new_revision();
|
||||||
let index = zalsa_mut.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>();
|
let index = zalsa_mut.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>();
|
||||||
let (ingredient, runtime) = zalsa_mut.lookup_ingredient_mut(index);
|
let (ingredient, runtime) = zalsa_mut.lookup_ingredient_mut(index);
|
||||||
|
@ -208,8 +207,10 @@ macro_rules! setup_input_struct {
|
||||||
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
|
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
|
||||||
$Db: ?Sized + $zalsa::Database,
|
$Db: ?Sized + $zalsa::Database,
|
||||||
{
|
{
|
||||||
let fields = $Configuration::ingredient_(db.zalsa()).field(
|
let (zalsa, zalsa_local) = db.zalsas();
|
||||||
db.as_dyn_database(),
|
let fields = $Configuration::ingredient_(zalsa).field(
|
||||||
|
zalsa,
|
||||||
|
zalsa_local,
|
||||||
self,
|
self,
|
||||||
$field_index,
|
$field_index,
|
||||||
);
|
);
|
||||||
|
@ -228,7 +229,8 @@ macro_rules! setup_input_struct {
|
||||||
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
|
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
|
||||||
$Db: ?Sized + $zalsa::Database,
|
$Db: ?Sized + $zalsa::Database,
|
||||||
{
|
{
|
||||||
let (ingredient, revision) = $Configuration::ingredient_mut(db.as_dyn_database_mut());
|
let zalsa = db.zalsa_mut();
|
||||||
|
let (ingredient, revision) = $Configuration::ingredient_mut(zalsa);
|
||||||
$zalsa::input::SetterImpl::new(
|
$zalsa::input::SetterImpl::new(
|
||||||
revision,
|
revision,
|
||||||
self,
|
self,
|
||||||
|
@ -267,7 +269,8 @@ macro_rules! setup_input_struct {
|
||||||
$(for<'__trivial_bounds> $field_ty: std::fmt::Debug),*
|
$(for<'__trivial_bounds> $field_ty: std::fmt::Debug),*
|
||||||
{
|
{
|
||||||
$zalsa::with_attached_database(|db| {
|
$zalsa::with_attached_database(|db| {
|
||||||
let fields = $Configuration::ingredient(db).leak_fields(db, this);
|
let zalsa = db.zalsa();
|
||||||
|
let fields = $Configuration::ingredient_(zalsa).leak_fields(zalsa, this);
|
||||||
let mut f = f.debug_struct(stringify!($Struct));
|
let mut f = f.debug_struct(stringify!($Struct));
|
||||||
let f = f.field("[salsa id]", &$zalsa::AsId::as_id(&this));
|
let f = f.field("[salsa id]", &$zalsa::AsId::as_id(&this));
|
||||||
$(
|
$(
|
||||||
|
@ -296,11 +299,11 @@ macro_rules! setup_input_struct {
|
||||||
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
|
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
|
||||||
$Db: ?Sized + salsa::Database
|
$Db: ?Sized + salsa::Database
|
||||||
{
|
{
|
||||||
let zalsa = db.zalsa();
|
let (zalsa, zalsa_local) = db.zalsas();
|
||||||
let current_revision = zalsa.current_revision();
|
let current_revision = zalsa.current_revision();
|
||||||
let ingredient = $Configuration::ingredient_(zalsa);
|
let ingredient = $Configuration::ingredient_(zalsa);
|
||||||
let (fields, revision, durabilities) = builder::builder_into_inner(self, current_revision);
|
let (fields, revision, durabilities) = builder::builder_into_inner(self, current_revision);
|
||||||
ingredient.new_input(db.as_dyn_database(), fields, revision, durabilities)
|
ingredient.new_input(zalsa, zalsa_local, fields, revision, durabilities)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,15 +149,11 @@ macro_rules! setup_interned_struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $Configuration {
|
impl $Configuration {
|
||||||
pub fn ingredient<Db>(db: &Db) -> &$zalsa_struct::IngredientImpl<Self>
|
pub fn ingredient(zalsa: &$zalsa::Zalsa) -> &$zalsa_struct::IngredientImpl<Self>
|
||||||
where
|
|
||||||
Db: ?Sized + $zalsa::Database,
|
|
||||||
{
|
{
|
||||||
static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
|
static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
|
||||||
$zalsa::IngredientCache::new();
|
$zalsa::IngredientCache::new();
|
||||||
|
|
||||||
let zalsa = db.zalsa();
|
|
||||||
|
|
||||||
// SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the only
|
// SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the only
|
||||||
// ingredient created by our jar is the struct ingredient.
|
// ingredient created by our jar is the struct ingredient.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -239,7 +235,8 @@ macro_rules! setup_interned_struct {
|
||||||
$field_ty: $zalsa::interned::HashEqLike<$indexed_ty>,
|
$field_ty: $zalsa::interned::HashEqLike<$indexed_ty>,
|
||||||
)*
|
)*
|
||||||
{
|
{
|
||||||
$Configuration::ingredient(db).intern(db.as_dyn_database(),
|
let (zalsa, zalsa_local) = db.zalsas();
|
||||||
|
$Configuration::ingredient(zalsa).intern(zalsa, zalsa_local,
|
||||||
StructKey::<$db_lt>($($field_id,)* std::marker::PhantomData::default()), |_, data| ($($zalsa::interned::Lookup::into_owned(data.$field_index),)*))
|
StructKey::<$db_lt>($($field_id,)* std::marker::PhantomData::default()), |_, data| ($($zalsa::interned::Lookup::into_owned(data.$field_index),)*))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +247,8 @@ macro_rules! setup_interned_struct {
|
||||||
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
|
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
|
||||||
$Db: ?Sized + $zalsa::Database,
|
$Db: ?Sized + $zalsa::Database,
|
||||||
{
|
{
|
||||||
let fields = $Configuration::ingredient(db).fields(db.as_dyn_database(), self);
|
let zalsa = db.zalsa();
|
||||||
|
let fields = $Configuration::ingredient(zalsa).fields(zalsa, self);
|
||||||
$zalsa::return_mode_expression!(
|
$zalsa::return_mode_expression!(
|
||||||
$field_option,
|
$field_option,
|
||||||
$field_ty,
|
$field_ty,
|
||||||
|
@ -262,7 +260,8 @@ macro_rules! setup_interned_struct {
|
||||||
/// Default debug formatting for this struct (may be useful if you define your own `Debug` impl)
|
/// 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 {
|
pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
$zalsa::with_attached_database(|db| {
|
$zalsa::with_attached_database(|db| {
|
||||||
let fields = $Configuration::ingredient(db).fields(db.as_dyn_database(), this);
|
let zalsa = db.zalsa();
|
||||||
|
let fields = $Configuration::ingredient(zalsa).fields(zalsa, this);
|
||||||
let mut f = f.debug_struct(stringify!($Struct));
|
let mut f = f.debug_struct(stringify!($Struct));
|
||||||
$(
|
$(
|
||||||
let f = f.field(stringify!($field_id), &fields.$field_index);
|
let f = f.field(stringify!($field_id), &fields.$field_index);
|
||||||
|
|
|
@ -175,17 +175,21 @@ macro_rules! setup_tracked_fn {
|
||||||
impl $Configuration {
|
impl $Configuration {
|
||||||
fn fn_ingredient(db: &dyn $Db) -> &$zalsa::function::IngredientImpl<$Configuration> {
|
fn fn_ingredient(db: &dyn $Db) -> &$zalsa::function::IngredientImpl<$Configuration> {
|
||||||
let zalsa = db.zalsa();
|
let zalsa = db.zalsa();
|
||||||
|
Self::fn_ingredient_(db, zalsa)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn fn_ingredient_<'z>(db: &dyn $Db, zalsa: &'z $zalsa::Zalsa) -> &'z $zalsa::function::IngredientImpl<$Configuration> {
|
||||||
// SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the first
|
// SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the first
|
||||||
// ingredient created by our jar is the function ingredient.
|
// ingredient created by our jar is the function ingredient.
|
||||||
unsafe {
|
unsafe {
|
||||||
$FN_CACHE.get_or_create(zalsa, || zalsa.lookup_jar_by_type::<$fn_name>())
|
$FN_CACHE.get_or_create(zalsa, || zalsa.lookup_jar_by_type::<$fn_name>())
|
||||||
}
|
}
|
||||||
.get_or_init(|| <dyn $Db as $Db>::zalsa_register_downcaster(db))
|
.get_or_init(|| *<dyn $Db as $Db>::zalsa_register_downcaster(db))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fn_ingredient_mut(db: &mut dyn $Db) -> &mut $zalsa::function::IngredientImpl<Self> {
|
pub fn fn_ingredient_mut(db: &mut dyn $Db) -> &mut $zalsa::function::IngredientImpl<Self> {
|
||||||
let view = <dyn $Db as $Db>::zalsa_register_downcaster(db);
|
let view = *<dyn $Db as $Db>::zalsa_register_downcaster(db);
|
||||||
let zalsa_mut = db.zalsa_mut();
|
let zalsa_mut = db.zalsa_mut();
|
||||||
let index = zalsa_mut.lookup_jar_by_type::<$fn_name>();
|
let index = zalsa_mut.lookup_jar_by_type::<$fn_name>();
|
||||||
let (ingredient, _) = zalsa_mut.lookup_ingredient_mut(index);
|
let (ingredient, _) = zalsa_mut.lookup_ingredient_mut(index);
|
||||||
|
@ -199,7 +203,12 @@ macro_rules! setup_tracked_fn {
|
||||||
db: &dyn $Db,
|
db: &dyn $Db,
|
||||||
) -> &$zalsa::interned::IngredientImpl<$Configuration> {
|
) -> &$zalsa::interned::IngredientImpl<$Configuration> {
|
||||||
let zalsa = db.zalsa();
|
let zalsa = db.zalsa();
|
||||||
|
Self::intern_ingredient_(zalsa)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn intern_ingredient_<'z>(
|
||||||
|
zalsa: &'z $zalsa::Zalsa
|
||||||
|
) -> &'z $zalsa::interned::IngredientImpl<$Configuration> {
|
||||||
// SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the second
|
// SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the second
|
||||||
// ingredient created by our jar is the interned ingredient (given `needs_interner`).
|
// ingredient created by our jar is the interned ingredient (given `needs_interner`).
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -257,12 +266,12 @@ macro_rules! setup_tracked_fn {
|
||||||
$($cycle_recovery_fn)*(db, value, count, $($input_id),*)
|
$($cycle_recovery_fn)*(db, value, count, $($input_id),*)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn id_to_input<$db_lt>(db: &$db_lt Self::DbView, key: salsa::Id) -> Self::Input<$db_lt> {
|
fn id_to_input<$db_lt>(zalsa: &$db_lt $zalsa::Zalsa, key: salsa::Id) -> Self::Input<$db_lt> {
|
||||||
$zalsa::macro_if! {
|
$zalsa::macro_if! {
|
||||||
if $needs_interner {
|
if $needs_interner {
|
||||||
$Configuration::intern_ingredient(db).data(db.as_dyn_database(), key).clone()
|
$Configuration::intern_ingredient_(zalsa).data(zalsa, key).clone()
|
||||||
} else {
|
} else {
|
||||||
$zalsa::FromIdWithDb::from_id(key, db.zalsa())
|
$zalsa::FromIdWithDb::from_id(key, zalsa)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -340,9 +349,10 @@ macro_rules! setup_tracked_fn {
|
||||||
) -> Vec<&$db_lt A> {
|
) -> Vec<&$db_lt A> {
|
||||||
use salsa::plumbing as $zalsa;
|
use salsa::plumbing as $zalsa;
|
||||||
let key = $zalsa::macro_if! {
|
let key = $zalsa::macro_if! {
|
||||||
if $needs_interner {
|
if $needs_interner {{
|
||||||
$Configuration::intern_ingredient($db).intern_id($db.as_dyn_database(), ($($input_id),*), |_, data| data)
|
let (zalsa, zalsa_local) = $db.zalsas();
|
||||||
} else {
|
$Configuration::intern_ingredient($db).intern_id(zalsa, zalsa_local, ($($input_id),*), |_, data| data)
|
||||||
|
}} else {
|
||||||
$zalsa::AsId::as_id(&($($input_id),*))
|
$zalsa::AsId::as_id(&($($input_id),*))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -380,14 +390,17 @@ macro_rules! setup_tracked_fn {
|
||||||
}
|
}
|
||||||
|
|
||||||
$zalsa::attach($db, || {
|
$zalsa::attach($db, || {
|
||||||
|
let (zalsa, zalsa_local) = $db.zalsas();
|
||||||
let result = $zalsa::macro_if! {
|
let result = $zalsa::macro_if! {
|
||||||
if $needs_interner {
|
if $needs_interner {
|
||||||
{
|
{
|
||||||
let key = $Configuration::intern_ingredient($db).intern_id($db.as_dyn_database(), ($($input_id),*), |_, data| data);
|
let key = $Configuration::intern_ingredient_(zalsa).intern_id(zalsa, zalsa_local, ($($input_id),*), |_, data| data);
|
||||||
$Configuration::fn_ingredient($db).fetch($db, key)
|
$Configuration::fn_ingredient_($db, zalsa).fetch($db, zalsa, zalsa_local, key)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$Configuration::fn_ingredient($db).fetch($db, $zalsa::AsId::as_id(&($($input_id),*)))
|
{
|
||||||
|
$Configuration::fn_ingredient_($db, zalsa).fetch($db, zalsa, zalsa_local, $zalsa::AsId::as_id(&($($input_id),*)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -282,8 +282,9 @@ macro_rules! setup_tracked_struct {
|
||||||
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
|
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
|
||||||
$Db: ?Sized + $zalsa::Database,
|
$Db: ?Sized + $zalsa::Database,
|
||||||
{
|
{
|
||||||
$Configuration::ingredient(db.as_dyn_database()).new_struct(
|
let (zalsa, zalsa_local) = db.zalsas();
|
||||||
db.as_dyn_database(),
|
$Configuration::ingredient_(zalsa).new_struct(
|
||||||
|
zalsa,zalsa_local,
|
||||||
($($field_id,)*)
|
($($field_id,)*)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -295,8 +296,8 @@ macro_rules! setup_tracked_struct {
|
||||||
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
|
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
|
||||||
$Db: ?Sized + $zalsa::Database,
|
$Db: ?Sized + $zalsa::Database,
|
||||||
{
|
{
|
||||||
let db = db.as_dyn_database();
|
let (zalsa, zalsa_local) = db.zalsas();
|
||||||
let fields = $Configuration::ingredient(db).tracked_field(db, self, $relative_tracked_index);
|
let fields = $Configuration::ingredient_(zalsa).tracked_field(zalsa, zalsa_local, self, $relative_tracked_index);
|
||||||
$crate::return_mode_expression!(
|
$crate::return_mode_expression!(
|
||||||
$tracked_option,
|
$tracked_option,
|
||||||
$tracked_ty,
|
$tracked_ty,
|
||||||
|
@ -312,8 +313,8 @@ macro_rules! setup_tracked_struct {
|
||||||
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
|
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
|
||||||
$Db: ?Sized + $zalsa::Database,
|
$Db: ?Sized + $zalsa::Database,
|
||||||
{
|
{
|
||||||
let db = db.as_dyn_database();
|
let zalsa = db.zalsa();
|
||||||
let fields = $Configuration::ingredient(db).untracked_field(db, self);
|
let fields = $Configuration::ingredient_(zalsa).untracked_field(zalsa, self);
|
||||||
$crate::return_mode_expression!(
|
$crate::return_mode_expression!(
|
||||||
$untracked_option,
|
$untracked_option,
|
||||||
$untracked_ty,
|
$untracked_ty,
|
||||||
|
@ -335,7 +336,8 @@ macro_rules! setup_tracked_struct {
|
||||||
$(for<$db_lt> $field_ty: std::fmt::Debug),*
|
$(for<$db_lt> $field_ty: std::fmt::Debug),*
|
||||||
{
|
{
|
||||||
$zalsa::with_attached_database(|db| {
|
$zalsa::with_attached_database(|db| {
|
||||||
let fields = $Configuration::ingredient(db).leak_fields(db, this);
|
let zalsa = db.zalsa();
|
||||||
|
let fields = $Configuration::ingredient_(zalsa).leak_fields(zalsa, this);
|
||||||
let mut f = f.debug_struct(stringify!($Struct));
|
let mut f = f.debug_struct(stringify!($Struct));
|
||||||
let f = f.field("[salsa id]", &$zalsa::AsId::as_id(&this));
|
let f = f.field("[salsa id]", &$zalsa::AsId::as_id(&this));
|
||||||
$(
|
$(
|
||||||
|
|
|
@ -110,18 +110,14 @@ impl DbMacro {
|
||||||
let trait_name = &input.ident;
|
let trait_name = &input.ident;
|
||||||
input.items.push(parse_quote! {
|
input.items.push(parse_quote! {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn zalsa_register_downcaster(&self) -> salsa::plumbing::DatabaseDownCaster<dyn #trait_name>;
|
fn zalsa_register_downcaster(&self) -> &salsa::plumbing::DatabaseDownCaster<dyn #trait_name>;
|
||||||
});
|
});
|
||||||
|
|
||||||
let comment = format!(" Downcast a [`dyn Database`] to a [`dyn {trait_name}`]");
|
let comment = format!(" downcast `Self` to a [`dyn {trait_name}`]");
|
||||||
input.items.push(parse_quote! {
|
input.items.push(parse_quote! {
|
||||||
#[doc = #comment]
|
#[doc = #comment]
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The input database must be of type `Self`.
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
unsafe fn downcast(db: &dyn salsa::plumbing::Database) -> &dyn #trait_name where Self: Sized;
|
fn downcast(&self) -> &dyn #trait_name where Self: Sized;
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -138,17 +134,17 @@ impl DbMacro {
|
||||||
#[cold]
|
#[cold]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn zalsa_register_downcaster(&self) -> salsa::plumbing::DatabaseDownCaster<dyn #TraitPath> {
|
fn zalsa_register_downcaster(&self) -> &salsa::plumbing::DatabaseDownCaster<dyn #TraitPath> {
|
||||||
salsa::plumbing::views(self).add(<Self as #TraitPath>::downcast)
|
salsa::plumbing::views(self).add::<Self, dyn #TraitPath>(unsafe {
|
||||||
|
::std::mem::transmute(<Self as #TraitPath>::downcast as fn(_) -> _)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
input.items.push(parse_quote! {
|
input.items.push(parse_quote! {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn downcast(db: &dyn salsa::plumbing::Database) -> &dyn #TraitPath where Self: Sized {
|
fn downcast(&self) -> &dyn #TraitPath where Self: Sized {
|
||||||
debug_assert_eq!(db.type_id(), ::core::any::TypeId::of::<Self>());
|
self
|
||||||
// SAFETY: The input database must be of type `Self`.
|
|
||||||
unsafe { &*salsa::plumbing::transmute_data_ptr::<dyn salsa::plumbing::Database, Self>(db) }
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -102,7 +102,8 @@ impl<A: Accumulator> Ingredient for IngredientImpl<A> {
|
||||||
|
|
||||||
unsafe fn maybe_changed_after(
|
unsafe fn maybe_changed_after(
|
||||||
&self,
|
&self,
|
||||||
_db: &dyn Database,
|
_zalsa: &crate::zalsa::Zalsa,
|
||||||
|
_db: crate::database::RawDatabase<'_>,
|
||||||
_input: Id,
|
_input: Id,
|
||||||
_revision: Revision,
|
_revision: Revision,
|
||||||
_cycle_heads: &mut CycleHeads,
|
_cycle_heads: &mut CycleHeads,
|
||||||
|
|
|
@ -1,13 +1,39 @@
|
||||||
use std::any::Any;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
use crate::views::DatabaseDownCaster;
|
use crate::views::DatabaseDownCaster;
|
||||||
use crate::zalsa::{IngredientIndex, ZalsaDatabase};
|
use crate::zalsa::{IngredientIndex, ZalsaDatabase};
|
||||||
use crate::{Durability, Revision};
|
use crate::{Durability, Revision};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct RawDatabase<'db> {
|
||||||
|
pub(crate) ptr: NonNull<()>,
|
||||||
|
_marker: std::marker::PhantomData<&'db dyn Database>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'db, Db: Database + ?Sized> From<&'db Db> for RawDatabase<'db> {
|
||||||
|
#[inline]
|
||||||
|
fn from(db: &'db Db) -> Self {
|
||||||
|
RawDatabase {
|
||||||
|
ptr: NonNull::from(db).cast(),
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'db, Db: Database + ?Sized> From<&'db mut Db> for RawDatabase<'db> {
|
||||||
|
#[inline]
|
||||||
|
fn from(db: &'db mut Db) -> Self {
|
||||||
|
RawDatabase {
|
||||||
|
ptr: NonNull::from(db).cast(),
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The trait implemented by all Salsa databases.
|
/// The trait implemented by all Salsa databases.
|
||||||
/// You can create your own subtraits of this trait using the `#[salsa::db]`(`crate::db`) procedural macro.
|
/// You can create your own subtraits of this trait using the `#[salsa::db]`(`crate::db`) procedural macro.
|
||||||
pub trait Database: Send + AsDynDatabase + Any + ZalsaDatabase {
|
pub trait Database: Send + ZalsaDatabase + AsDynDatabase {
|
||||||
/// Enforces current LRU limits, evicting entries if necessary.
|
/// Enforces current LRU limits, evicting entries if necessary.
|
||||||
///
|
///
|
||||||
/// **WARNING:** Just like an ordinary write, this method triggers
|
/// **WARNING:** Just like an ordinary write, this method triggers
|
||||||
|
@ -84,28 +110,27 @@ pub trait Database: Send + AsDynDatabase + Any + ZalsaDatabase {
|
||||||
#[cold]
|
#[cold]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn zalsa_register_downcaster(&self) -> DatabaseDownCaster<dyn Database> {
|
fn zalsa_register_downcaster(&self) -> &DatabaseDownCaster<dyn Database> {
|
||||||
self.zalsa().views().downcaster_for::<dyn Database>()
|
self.zalsa().views().downcaster_for::<dyn Database>()
|
||||||
// The no-op downcaster is special cased in view caster construction.
|
// The no-op downcaster is special cased in view caster construction.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn downcast(db: &dyn Database) -> &dyn Database
|
fn downcast(&self) -> &dyn Database
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
// No-op
|
// No-op
|
||||||
db
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Upcast to a `dyn Database`.
|
/// Upcast to a `dyn Database`.
|
||||||
///
|
///
|
||||||
/// Only required because upcasts not yet stabilized (*grr*).
|
/// Only required because upcasting does not work for unsized generic parameters.
|
||||||
pub trait AsDynDatabase {
|
pub trait AsDynDatabase {
|
||||||
fn as_dyn_database(&self) -> &dyn Database;
|
fn as_dyn_database(&self) -> &dyn Database;
|
||||||
fn as_dyn_database_mut(&mut self) -> &mut dyn Database;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Database> AsDynDatabase for T {
|
impl<T: Database> AsDynDatabase for T {
|
||||||
|
@ -113,30 +138,12 @@ impl<T: Database> AsDynDatabase for T {
|
||||||
fn as_dyn_database(&self) -> &dyn Database {
|
fn as_dyn_database(&self) -> &dyn Database {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn as_dyn_database_mut(&mut self) -> &mut dyn Database {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_revision<Db: ?Sized + Database>(db: &Db) -> Revision {
|
pub fn current_revision<Db: ?Sized + Database>(db: &Db) -> Revision {
|
||||||
db.zalsa().current_revision()
|
db.zalsa().current_revision()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl dyn Database {
|
|
||||||
/// Upcasts `self` to the given view.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// If the view has not been added to the database (see [`crate::views::Views`]).
|
|
||||||
#[track_caller]
|
|
||||||
pub fn as_view<DbView: ?Sized + Database>(&self) -> &DbView {
|
|
||||||
let views = self.zalsa().views();
|
|
||||||
views.downcaster_for().downcast(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "salsa_unstable")]
|
#[cfg(feature = "salsa_unstable")]
|
||||||
pub use memory_usage::IngredientInfo;
|
pub use memory_usage::IngredientInfo;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::accumulator::accumulated_map::{AccumulatedMap, InputAccumulatedValues
|
||||||
use crate::cycle::{
|
use crate::cycle::{
|
||||||
empty_cycle_heads, CycleHeads, CycleRecoveryAction, CycleRecoveryStrategy, ProvisionalStatus,
|
empty_cycle_heads, CycleHeads, CycleRecoveryAction, CycleRecoveryStrategy, ProvisionalStatus,
|
||||||
};
|
};
|
||||||
|
use crate::database::RawDatabase;
|
||||||
use crate::function::delete::DeletedEntries;
|
use crate::function::delete::DeletedEntries;
|
||||||
use crate::function::sync::{ClaimResult, SyncTable};
|
use crate::function::sync::{ClaimResult, SyncTable};
|
||||||
use crate::ingredient::{Ingredient, WaitForResult};
|
use crate::ingredient::{Ingredient, WaitForResult};
|
||||||
|
@ -22,7 +23,7 @@ use crate::table::Table;
|
||||||
use crate::views::DatabaseDownCaster;
|
use crate::views::DatabaseDownCaster;
|
||||||
use crate::zalsa::{IngredientIndex, MemoIngredientIndex, Zalsa};
|
use crate::zalsa::{IngredientIndex, MemoIngredientIndex, Zalsa};
|
||||||
use crate::zalsa_local::QueryOriginRef;
|
use crate::zalsa_local::QueryOriginRef;
|
||||||
use crate::{Database, Id, Revision};
|
use crate::{Id, Revision};
|
||||||
|
|
||||||
mod accumulated;
|
mod accumulated;
|
||||||
mod backdate;
|
mod backdate;
|
||||||
|
@ -68,10 +69,9 @@ pub trait Configuration: Any {
|
||||||
/// This invokes user code in form of the `Eq` impl.
|
/// This invokes user code in form of the `Eq` impl.
|
||||||
fn values_equal<'db>(old_value: &Self::Output<'db>, new_value: &Self::Output<'db>) -> bool;
|
fn values_equal<'db>(old_value: &Self::Output<'db>, new_value: &Self::Output<'db>) -> bool;
|
||||||
|
|
||||||
// FIXME: This should take a `&Zalsa`
|
|
||||||
/// Convert from the id used internally to the value that execute is expecting.
|
/// Convert from the id used internally to the value that execute is expecting.
|
||||||
/// This is a no-op if the input to the function is a salsa struct.
|
/// This is a no-op if the input to the function is a salsa struct.
|
||||||
fn id_to_input(db: &Self::DbView, key: Id) -> Self::Input<'_>;
|
fn id_to_input(zalsa: &Zalsa, key: Id) -> Self::Input<'_>;
|
||||||
|
|
||||||
/// Returns the size of any heap allocations in the output value, in bytes.
|
/// Returns the size of any heap allocations in the output value, in bytes.
|
||||||
fn heap_size(_value: &Self::Output<'_>) -> usize {
|
fn heap_size(_value: &Self::Output<'_>) -> usize {
|
||||||
|
@ -124,7 +124,7 @@ pub struct IngredientImpl<C: Configuration> {
|
||||||
/// Used to find memos to throw out when we have too many memoized values.
|
/// Used to find memos to throw out when we have too many memoized values.
|
||||||
lru: lru::Lru,
|
lru: lru::Lru,
|
||||||
|
|
||||||
/// A downcaster from `dyn Database` to `C::DbView`.
|
/// An downcaster to `C::DbView`.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
|
@ -261,7 +261,8 @@ where
|
||||||
|
|
||||||
unsafe fn maybe_changed_after(
|
unsafe fn maybe_changed_after(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn Database,
|
_zalsa: &crate::zalsa::Zalsa,
|
||||||
|
db: RawDatabase<'_>,
|
||||||
input: Id,
|
input: Id,
|
||||||
revision: Revision,
|
revision: Revision,
|
||||||
cycle_heads: &mut CycleHeads,
|
cycle_heads: &mut CycleHeads,
|
||||||
|
@ -370,12 +371,13 @@ where
|
||||||
C::CYCLE_STRATEGY
|
C::CYCLE_STRATEGY
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accumulated<'db>(
|
unsafe fn accumulated<'db>(
|
||||||
&'db self,
|
&'db self,
|
||||||
db: &'db dyn Database,
|
db: RawDatabase<'db>,
|
||||||
key_index: Id,
|
key_index: Id,
|
||||||
) -> (Option<&'db AccumulatedMap>, InputAccumulatedValues) {
|
) -> (Option<&'db AccumulatedMap>, InputAccumulatedValues) {
|
||||||
let db = self.view_caster().downcast(db);
|
// SAFETY: The `db` belongs to the ingredient as per caller invariant
|
||||||
|
let db = unsafe { self.view_caster().downcast_unchecked(db) };
|
||||||
self.accumulated_map(db, key_index)
|
self.accumulated_map(db, key_index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::function::{Configuration, IngredientImpl};
|
||||||
use crate::hash::FxHashSet;
|
use crate::hash::FxHashSet;
|
||||||
use crate::zalsa::ZalsaDatabase;
|
use crate::zalsa::ZalsaDatabase;
|
||||||
use crate::zalsa_local::QueryOriginRef;
|
use crate::zalsa_local::QueryOriginRef;
|
||||||
use crate::{AsDynDatabase, DatabaseKeyIndex, Id};
|
use crate::{DatabaseKeyIndex, Id};
|
||||||
|
|
||||||
impl<C> IngredientImpl<C>
|
impl<C> IngredientImpl<C>
|
||||||
where
|
where
|
||||||
|
@ -37,9 +37,8 @@ where
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
|
|
||||||
// First ensure the result is up to date
|
// First ensure the result is up to date
|
||||||
self.fetch(db, key);
|
self.fetch(db, zalsa, zalsa_local, key);
|
||||||
|
|
||||||
let db = db.as_dyn_database();
|
|
||||||
let db_key = self.database_key_index(key);
|
let db_key = self.database_key_index(key);
|
||||||
let mut visited: FxHashSet<DatabaseKeyIndex> = FxHashSet::default();
|
let mut visited: FxHashSet<DatabaseKeyIndex> = FxHashSet::default();
|
||||||
let mut stack: Vec<DatabaseKeyIndex> = vec![db_key];
|
let mut stack: Vec<DatabaseKeyIndex> = vec![db_key];
|
||||||
|
@ -54,7 +53,9 @@ where
|
||||||
|
|
||||||
let ingredient = zalsa.lookup_ingredient(k.ingredient_index());
|
let ingredient = zalsa.lookup_ingredient(k.ingredient_index());
|
||||||
// Extend `output` with any values accumulated by `k`.
|
// Extend `output` with any values accumulated by `k`.
|
||||||
let (accumulated_map, input) = ingredient.accumulated(db, k.key_index());
|
// SAFETY: `db` owns the `ingredient`
|
||||||
|
let (accumulated_map, input) =
|
||||||
|
unsafe { ingredient.accumulated(db.into(), k.key_index()) };
|
||||||
if let Some(accumulated_map) = accumulated_map {
|
if let Some(accumulated_map) = accumulated_map {
|
||||||
accumulated_map.extend_with_accumulated(accumulator.index(), &mut output);
|
accumulated_map.extend_with_accumulated(accumulator.index(), &mut output);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::function::{Configuration, IngredientImpl};
|
||||||
use crate::sync::atomic::{AtomicBool, Ordering};
|
use crate::sync::atomic::{AtomicBool, Ordering};
|
||||||
use crate::zalsa::{MemoIngredientIndex, Zalsa, ZalsaDatabase};
|
use crate::zalsa::{MemoIngredientIndex, Zalsa, ZalsaDatabase};
|
||||||
use crate::zalsa_local::{ActiveQueryGuard, QueryRevisions};
|
use crate::zalsa_local::{ActiveQueryGuard, QueryRevisions};
|
||||||
use crate::{Event, EventKind, Id, Revision};
|
use crate::{Event, EventKind, Id};
|
||||||
|
|
||||||
impl<C> IngredientImpl<C>
|
impl<C> IngredientImpl<C>
|
||||||
where
|
where
|
||||||
|
@ -41,16 +41,11 @@ where
|
||||||
|
|
||||||
let (new_value, mut revisions) = match C::CYCLE_STRATEGY {
|
let (new_value, mut revisions) = match C::CYCLE_STRATEGY {
|
||||||
CycleRecoveryStrategy::Panic => {
|
CycleRecoveryStrategy::Panic => {
|
||||||
Self::execute_query(db, active_query, opt_old_memo, zalsa.current_revision(), id)
|
Self::execute_query(db, zalsa, active_query, opt_old_memo, id)
|
||||||
}
|
}
|
||||||
CycleRecoveryStrategy::FallbackImmediate => {
|
CycleRecoveryStrategy::FallbackImmediate => {
|
||||||
let (mut new_value, mut revisions) = Self::execute_query(
|
let (mut new_value, mut revisions) =
|
||||||
db,
|
Self::execute_query(db, zalsa, active_query, opt_old_memo, id);
|
||||||
active_query,
|
|
||||||
opt_old_memo,
|
|
||||||
zalsa.current_revision(),
|
|
||||||
id,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(cycle_heads) = revisions.cycle_heads_mut() {
|
if let Some(cycle_heads) = revisions.cycle_heads_mut() {
|
||||||
// Did the new result we got depend on our own provisional value, in a cycle?
|
// Did the new result we got depend on our own provisional value, in a cycle?
|
||||||
|
@ -77,7 +72,7 @@ where
|
||||||
let active_query = db
|
let active_query = db
|
||||||
.zalsa_local()
|
.zalsa_local()
|
||||||
.push_query(database_key_index, IterationCount::initial());
|
.push_query(database_key_index, IterationCount::initial());
|
||||||
new_value = C::cycle_initial(db, C::id_to_input(db, id));
|
new_value = C::cycle_initial(db, C::id_to_input(zalsa, id));
|
||||||
revisions = active_query.pop();
|
revisions = active_query.pop();
|
||||||
// We need to set `cycle_heads` and `verified_final` because it needs to propagate to the callers.
|
// We need to set `cycle_heads` and `verified_final` because it needs to propagate to the callers.
|
||||||
// When verifying this, we will see we have fallback and mark ourselves verified.
|
// When verifying this, we will see we have fallback and mark ourselves verified.
|
||||||
|
@ -136,13 +131,8 @@ where
|
||||||
let mut opt_last_provisional: Option<&Memo<'db, C>> = None;
|
let mut opt_last_provisional: Option<&Memo<'db, C>> = None;
|
||||||
loop {
|
loop {
|
||||||
let previous_memo = opt_last_provisional.or(opt_old_memo);
|
let previous_memo = opt_last_provisional.or(opt_old_memo);
|
||||||
let (mut new_value, mut revisions) = Self::execute_query(
|
let (mut new_value, mut revisions) =
|
||||||
db,
|
Self::execute_query(db, zalsa, active_query, previous_memo, id);
|
||||||
active_query,
|
|
||||||
previous_memo,
|
|
||||||
zalsa.current_revision(),
|
|
||||||
id,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Did the new result we got depend on our own provisional value, in a cycle?
|
// Did the new result we got depend on our own provisional value, in a cycle?
|
||||||
if let Some(cycle_heads) = revisions
|
if let Some(cycle_heads) = revisions
|
||||||
|
@ -192,7 +182,7 @@ where
|
||||||
db,
|
db,
|
||||||
&new_value,
|
&new_value,
|
||||||
iteration_count.as_u32(),
|
iteration_count.as_u32(),
|
||||||
C::id_to_input(db, id),
|
C::id_to_input(zalsa, id),
|
||||||
) {
|
) {
|
||||||
crate::CycleRecoveryAction::Iterate => {}
|
crate::CycleRecoveryAction::Iterate => {}
|
||||||
crate::CycleRecoveryAction::Fallback(fallback_value) => {
|
crate::CycleRecoveryAction::Fallback(fallback_value) => {
|
||||||
|
@ -258,9 +248,9 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
fn execute_query<'db>(
|
fn execute_query<'db>(
|
||||||
db: &'db C::DbView,
|
db: &'db C::DbView,
|
||||||
|
zalsa: &'db Zalsa,
|
||||||
active_query: ActiveQueryGuard<'db>,
|
active_query: ActiveQueryGuard<'db>,
|
||||||
opt_old_memo: Option<&Memo<'db, C>>,
|
opt_old_memo: Option<&Memo<'db, C>>,
|
||||||
current_revision: Revision,
|
|
||||||
id: Id,
|
id: Id,
|
||||||
) -> (C::Output<'db>, QueryRevisions) {
|
) -> (C::Output<'db>, QueryRevisions) {
|
||||||
if let Some(old_memo) = opt_old_memo {
|
if let Some(old_memo) = opt_old_memo {
|
||||||
|
@ -275,14 +265,16 @@ where
|
||||||
// * ensure that tracked struct created during the previous iteration
|
// * ensure that tracked struct created during the previous iteration
|
||||||
// (and are owned by the query) are alive even if the query in this iteration no longer creates them.
|
// (and are owned by the query) are alive even if the query in this iteration no longer creates them.
|
||||||
// * ensure the final returned memo depends on all inputs from all iterations.
|
// * ensure the final returned memo depends on all inputs from all iterations.
|
||||||
if old_memo.may_be_provisional() && old_memo.verified_at.load() == current_revision {
|
if old_memo.may_be_provisional()
|
||||||
|
&& old_memo.verified_at.load() == zalsa.current_revision()
|
||||||
|
{
|
||||||
active_query.seed_iteration(&old_memo.revisions);
|
active_query.seed_iteration(&old_memo.revisions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query was not previously executed, or value is potentially
|
// Query was not previously executed, or value is potentially
|
||||||
// stale, or value is absent. Let's execute!
|
// stale, or value is absent. Let's execute!
|
||||||
let new_value = C::execute(db, C::id_to_input(db, id));
|
let new_value = C::execute(db, C::id_to_input(zalsa, id));
|
||||||
|
|
||||||
(new_value, active_query.pop())
|
(new_value, active_query.pop())
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::cycle::{CycleHeads, CycleRecoveryStrategy, IterationCount};
|
||||||
use crate::function::memo::Memo;
|
use crate::function::memo::Memo;
|
||||||
use crate::function::sync::ClaimResult;
|
use crate::function::sync::ClaimResult;
|
||||||
use crate::function::{Configuration, IngredientImpl, VerifyResult};
|
use crate::function::{Configuration, IngredientImpl, VerifyResult};
|
||||||
use crate::zalsa::{MemoIngredientIndex, Zalsa, ZalsaDatabase};
|
use crate::zalsa::{MemoIngredientIndex, Zalsa};
|
||||||
use crate::zalsa_local::{QueryRevisions, ZalsaLocal};
|
use crate::zalsa_local::{QueryRevisions, ZalsaLocal};
|
||||||
use crate::Id;
|
use crate::Id;
|
||||||
|
|
||||||
|
@ -10,8 +10,13 @@ impl<C> IngredientImpl<C>
|
||||||
where
|
where
|
||||||
C: Configuration,
|
C: Configuration,
|
||||||
{
|
{
|
||||||
pub fn fetch<'db>(&'db self, db: &'db C::DbView, id: Id) -> &'db C::Output<'db> {
|
pub fn fetch<'db>(
|
||||||
let (zalsa, zalsa_local) = db.zalsas();
|
&'db self,
|
||||||
|
db: &'db C::DbView,
|
||||||
|
zalsa: &'db Zalsa,
|
||||||
|
zalsa_local: &'db ZalsaLocal,
|
||||||
|
id: Id,
|
||||||
|
) -> &'db C::Output<'db> {
|
||||||
zalsa.unwind_if_revision_cancelled(zalsa_local);
|
zalsa.unwind_if_revision_cancelled(zalsa_local);
|
||||||
|
|
||||||
let database_key_index = self.database_key_index(id);
|
let database_key_index = self.database_key_index(id);
|
||||||
|
@ -175,7 +180,7 @@ where
|
||||||
inserting and returning fixpoint initial value"
|
inserting and returning fixpoint initial value"
|
||||||
);
|
);
|
||||||
let revisions = QueryRevisions::fixpoint_initial(database_key_index);
|
let revisions = QueryRevisions::fixpoint_initial(database_key_index);
|
||||||
let initial_value = C::cycle_initial(db, C::id_to_input(db, id));
|
let initial_value = C::cycle_initial(db, C::id_to_input(zalsa, id));
|
||||||
Some(self.insert_memo(
|
Some(self.insert_memo(
|
||||||
zalsa,
|
zalsa,
|
||||||
id,
|
id,
|
||||||
|
@ -189,7 +194,7 @@ where
|
||||||
);
|
);
|
||||||
let active_query =
|
let active_query =
|
||||||
zalsa_local.push_query(database_key_index, IterationCount::initial());
|
zalsa_local.push_query(database_key_index, IterationCount::initial());
|
||||||
let fallback_value = C::cycle_initial(db, C::id_to_input(db, id));
|
let fallback_value = C::cycle_initial(db, C::id_to_input(zalsa, id));
|
||||||
let mut revisions = active_query.pop();
|
let mut revisions = active_query.pop();
|
||||||
revisions.set_cycle_heads(CycleHeads::initial(database_key_index));
|
revisions.set_cycle_heads(CycleHeads::initial(database_key_index));
|
||||||
// We need this for `cycle_heads()` to work. We will unset this in the outer `execute()`.
|
// We need this for `cycle_heads()` to work. We will unset this in the outer `execute()`.
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::key::DatabaseKeyIndex;
|
||||||
use crate::sync::atomic::Ordering;
|
use crate::sync::atomic::Ordering;
|
||||||
use crate::zalsa::{MemoIngredientIndex, Zalsa, ZalsaDatabase};
|
use crate::zalsa::{MemoIngredientIndex, Zalsa, ZalsaDatabase};
|
||||||
use crate::zalsa_local::{QueryEdgeKind, QueryOriginRef, ZalsaLocal};
|
use crate::zalsa_local::{QueryEdgeKind, QueryOriginRef, ZalsaLocal};
|
||||||
use crate::{AsDynDatabase as _, Id, Revision};
|
use crate::{Id, Revision};
|
||||||
|
|
||||||
/// Result of memo validation.
|
/// Result of memo validation.
|
||||||
pub enum VerifyResult {
|
pub enum VerifyResult {
|
||||||
|
@ -434,8 +434,6 @@ where
|
||||||
return VerifyResult::Changed;
|
return VerifyResult::Changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dyn_db = db.as_dyn_database();
|
|
||||||
|
|
||||||
let mut inputs = InputAccumulatedValues::Empty;
|
let mut inputs = InputAccumulatedValues::Empty;
|
||||||
// Fully tracked inputs? Iterate over the inputs and check them, one by one.
|
// Fully tracked inputs? Iterate over the inputs and check them, one by one.
|
||||||
//
|
//
|
||||||
|
@ -447,7 +445,7 @@ where
|
||||||
match edge.kind() {
|
match edge.kind() {
|
||||||
QueryEdgeKind::Input(dependency_index) => {
|
QueryEdgeKind::Input(dependency_index) => {
|
||||||
match dependency_index.maybe_changed_after(
|
match dependency_index.maybe_changed_after(
|
||||||
dyn_db,
|
db.into(),
|
||||||
zalsa,
|
zalsa,
|
||||||
old_memo.verified_at.load(),
|
old_memo.verified_at.load(),
|
||||||
cycle_heads,
|
cycle_heads,
|
||||||
|
|
|
@ -497,7 +497,7 @@ mod _memory_usage {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn id_to_input(_: &Self::DbView, _: Id) -> Self::Input<'_> {
|
fn id_to_input(_: &Zalsa, _: Id) -> Self::Input<'_> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::accumulator::accumulated_map::{AccumulatedMap, InputAccumulatedValues
|
||||||
use crate::cycle::{
|
use crate::cycle::{
|
||||||
empty_cycle_heads, CycleHeads, CycleRecoveryStrategy, IterationCount, ProvisionalStatus,
|
empty_cycle_heads, CycleHeads, CycleRecoveryStrategy, IterationCount, ProvisionalStatus,
|
||||||
};
|
};
|
||||||
|
use crate::database::RawDatabase;
|
||||||
use crate::function::VerifyResult;
|
use crate::function::VerifyResult;
|
||||||
use crate::runtime::Running;
|
use crate::runtime::Running;
|
||||||
use crate::sync::Arc;
|
use crate::sync::Arc;
|
||||||
|
@ -12,7 +13,7 @@ use crate::table::memo::MemoTableTypes;
|
||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
use crate::zalsa::{transmute_data_mut_ptr, transmute_data_ptr, IngredientIndex, Zalsa};
|
use crate::zalsa::{transmute_data_mut_ptr, transmute_data_ptr, IngredientIndex, Zalsa};
|
||||||
use crate::zalsa_local::QueryOriginRef;
|
use crate::zalsa_local::QueryOriginRef;
|
||||||
use crate::{Database, DatabaseKeyIndex, Id, Revision};
|
use crate::{DatabaseKeyIndex, Id, Revision};
|
||||||
|
|
||||||
/// A "jar" is a group of ingredients that are added atomically.
|
/// A "jar" is a group of ingredients that are added atomically.
|
||||||
///
|
///
|
||||||
|
@ -45,9 +46,10 @@ pub trait Ingredient: Any + std::fmt::Debug + Send + Sync {
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The passed in database needs to be the same one that the ingredient was created with.
|
/// The passed in database needs to be the same one that the ingredient was created with.
|
||||||
unsafe fn maybe_changed_after<'db>(
|
unsafe fn maybe_changed_after(
|
||||||
&'db self,
|
&self,
|
||||||
db: &'db dyn Database,
|
zalsa: &crate::zalsa::Zalsa,
|
||||||
|
db: crate::database::RawDatabase<'_>,
|
||||||
input: Id,
|
input: Id,
|
||||||
revision: Revision,
|
revision: Revision,
|
||||||
cycle_heads: &mut CycleHeads,
|
cycle_heads: &mut CycleHeads,
|
||||||
|
@ -159,9 +161,13 @@ pub trait Ingredient: Any + std::fmt::Debug + Send + Sync {
|
||||||
|
|
||||||
/// What values were accumulated during the creation of the value at `key_index`
|
/// What values were accumulated during the creation of the value at `key_index`
|
||||||
/// (if any).
|
/// (if any).
|
||||||
fn accumulated<'db>(
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The passed in database needs to be the same one that the ingredient was created with.
|
||||||
|
unsafe fn accumulated<'db>(
|
||||||
&'db self,
|
&'db self,
|
||||||
db: &'db dyn Database,
|
db: RawDatabase<'db>,
|
||||||
key_index: Id,
|
key_index: Id,
|
||||||
) -> (Option<&'db AccumulatedMap>, InputAccumulatedValues) {
|
) -> (Option<&'db AccumulatedMap>, InputAccumulatedValues) {
|
||||||
let _ = (db, key_index);
|
let _ = (db, key_index);
|
||||||
|
@ -171,7 +177,7 @@ pub trait Ingredient: Any + std::fmt::Debug + Send + Sync {
|
||||||
/// Returns memory usage information about any instances of the ingredient,
|
/// Returns memory usage information about any instances of the ingredient,
|
||||||
/// if applicable.
|
/// if applicable.
|
||||||
#[cfg(feature = "salsa_unstable")]
|
#[cfg(feature = "salsa_unstable")]
|
||||||
fn memory_usage(&self, _db: &dyn Database) -> Option<Vec<crate::database::SlotInfo>> {
|
fn memory_usage(&self, _db: &dyn crate::Database) -> Option<Vec<crate::database::SlotInfo>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,7 +185,7 @@ pub trait Ingredient: Any + std::fmt::Debug + Send + Sync {
|
||||||
impl dyn Ingredient {
|
impl dyn Ingredient {
|
||||||
/// Equivalent to the `downcast` method on `Any`.
|
/// Equivalent to the `downcast` method on `Any`.
|
||||||
///
|
///
|
||||||
/// Because we do not have dyn-upcasting support, we need this workaround.
|
/// Because we do not have dyn-downcasting support, we need this workaround.
|
||||||
pub fn assert_type<T: Any>(&self) -> &T {
|
pub fn assert_type<T: Any>(&self) -> &T {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
self.type_id(),
|
self.type_id(),
|
||||||
|
@ -195,7 +201,7 @@ impl dyn Ingredient {
|
||||||
|
|
||||||
/// Equivalent to the `downcast` methods on `Any`.
|
/// Equivalent to the `downcast` methods on `Any`.
|
||||||
///
|
///
|
||||||
/// Because we do not have dyn-upcasting support, we need this workaround.
|
/// Because we do not have dyn-downcasting support, we need this workaround.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
|
@ -214,7 +220,7 @@ impl dyn Ingredient {
|
||||||
|
|
||||||
/// Equivalent to the `downcast` method on `Any`.
|
/// Equivalent to the `downcast` method on `Any`.
|
||||||
///
|
///
|
||||||
/// Because we do not have dyn-upcasting support, we need this workaround.
|
/// Because we do not have dyn-downcasting support, we need this workaround.
|
||||||
pub fn assert_type_mut<T: Any>(&mut self) -> &mut T {
|
pub fn assert_type_mut<T: Any>(&mut self) -> &mut T {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Any::type_id(self),
|
Any::type_id(self),
|
||||||
|
|
28
src/input.rs
28
src/input.rs
|
@ -19,7 +19,7 @@ use crate::sync::Arc;
|
||||||
use crate::table::memo::{MemoTable, MemoTableTypes};
|
use crate::table::memo::{MemoTable, MemoTableTypes};
|
||||||
use crate::table::{Slot, Table};
|
use crate::table::{Slot, Table};
|
||||||
use crate::zalsa::{IngredientIndex, Zalsa};
|
use crate::zalsa::{IngredientIndex, Zalsa};
|
||||||
use crate::{Database, Durability, Id, Revision, Runtime};
|
use crate::{zalsa_local, Durability, Id, Revision, Runtime};
|
||||||
|
|
||||||
pub trait Configuration: Any {
|
pub trait Configuration: Any {
|
||||||
const DEBUG_NAME: &'static str;
|
const DEBUG_NAME: &'static str;
|
||||||
|
@ -104,13 +104,12 @@ impl<C: Configuration> IngredientImpl<C> {
|
||||||
|
|
||||||
pub fn new_input(
|
pub fn new_input(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn Database,
|
zalsa: &Zalsa,
|
||||||
|
zalsa_local: &zalsa_local::ZalsaLocal,
|
||||||
fields: C::Fields,
|
fields: C::Fields,
|
||||||
revisions: C::Revisions,
|
revisions: C::Revisions,
|
||||||
durabilities: C::Durabilities,
|
durabilities: C::Durabilities,
|
||||||
) -> C::Struct {
|
) -> C::Struct {
|
||||||
let (zalsa, zalsa_local) = db.zalsas();
|
|
||||||
|
|
||||||
let id = self.singleton.with_scope(|| {
|
let id = self.singleton.with_scope(|| {
|
||||||
zalsa_local.allocate(zalsa, self.ingredient_index, |_| Value::<C> {
|
zalsa_local.allocate(zalsa, self.ingredient_index, |_| Value::<C> {
|
||||||
fields,
|
fields,
|
||||||
|
@ -177,11 +176,11 @@ impl<C: Configuration> IngredientImpl<C> {
|
||||||
/// The caller is responsible for selecting the appropriate element.
|
/// The caller is responsible for selecting the appropriate element.
|
||||||
pub fn field<'db>(
|
pub fn field<'db>(
|
||||||
&'db self,
|
&'db self,
|
||||||
db: &'db dyn crate::Database,
|
zalsa: &'db Zalsa,
|
||||||
|
zalsa_local: &'db zalsa_local::ZalsaLocal,
|
||||||
id: C::Struct,
|
id: C::Struct,
|
||||||
field_index: usize,
|
field_index: usize,
|
||||||
) -> &'db C::Fields {
|
) -> &'db C::Fields {
|
||||||
let (zalsa, zalsa_local) = db.zalsas();
|
|
||||||
let field_ingredient_index = self.ingredient_index.successor(field_index);
|
let field_ingredient_index = self.ingredient_index.successor(field_index);
|
||||||
let id = id.as_id();
|
let id = id.as_id();
|
||||||
let value = Self::data(zalsa, id);
|
let value = Self::data(zalsa, id);
|
||||||
|
@ -197,17 +196,13 @@ impl<C: Configuration> IngredientImpl<C> {
|
||||||
|
|
||||||
#[cfg(feature = "salsa_unstable")]
|
#[cfg(feature = "salsa_unstable")]
|
||||||
/// Returns all data corresponding to the input struct.
|
/// Returns all data corresponding to the input struct.
|
||||||
pub fn entries<'db>(
|
pub fn entries<'db>(&'db self, zalsa: &'db Zalsa) -> impl Iterator<Item = &'db Value<C>> {
|
||||||
&'db self,
|
zalsa.table().slots_of::<Value<C>>()
|
||||||
db: &'db dyn crate::Database,
|
|
||||||
) -> impl Iterator<Item = &'db Value<C>> {
|
|
||||||
db.zalsa().table().slots_of::<Value<C>>()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Peek at the field values without recording any read dependency.
|
/// Peek at the field values without recording any read dependency.
|
||||||
/// Used for debug printouts.
|
/// Used for debug printouts.
|
||||||
pub fn leak_fields<'db>(&'db self, db: &'db dyn Database, id: C::Struct) -> &'db C::Fields {
|
pub fn leak_fields<'db>(&'db self, zalsa: &'db Zalsa, id: C::Struct) -> &'db C::Fields {
|
||||||
let zalsa = db.zalsa();
|
|
||||||
let id = id.as_id();
|
let id = id.as_id();
|
||||||
let value = Self::data(zalsa, id);
|
let value = Self::data(zalsa, id);
|
||||||
&value.fields
|
&value.fields
|
||||||
|
@ -225,7 +220,8 @@ impl<C: Configuration> Ingredient for IngredientImpl<C> {
|
||||||
|
|
||||||
unsafe fn maybe_changed_after(
|
unsafe fn maybe_changed_after(
|
||||||
&self,
|
&self,
|
||||||
_db: &dyn Database,
|
_zalsa: &crate::zalsa::Zalsa,
|
||||||
|
_db: crate::database::RawDatabase<'_>,
|
||||||
_input: Id,
|
_input: Id,
|
||||||
_revision: Revision,
|
_revision: Revision,
|
||||||
_cycle_heads: &mut CycleHeads,
|
_cycle_heads: &mut CycleHeads,
|
||||||
|
@ -249,9 +245,9 @@ impl<C: Configuration> Ingredient for IngredientImpl<C> {
|
||||||
|
|
||||||
/// Returns memory usage information about any inputs.
|
/// Returns memory usage information about any inputs.
|
||||||
#[cfg(feature = "salsa_unstable")]
|
#[cfg(feature = "salsa_unstable")]
|
||||||
fn memory_usage(&self, db: &dyn Database) -> Option<Vec<crate::database::SlotInfo>> {
|
fn memory_usage(&self, db: &dyn crate::Database) -> Option<Vec<crate::database::SlotInfo>> {
|
||||||
let memory_usage = self
|
let memory_usage = self
|
||||||
.entries(db)
|
.entries(db.zalsa())
|
||||||
// SAFETY: The memo table belongs to a value that we allocated, so it
|
// SAFETY: The memo table belongs to a value that we allocated, so it
|
||||||
// has the correct type.
|
// has the correct type.
|
||||||
.map(|value| unsafe { value.memory_usage(&self.memo_table_types) })
|
.map(|value| unsafe { value.memory_usage(&self.memo_table_types) })
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::input::{Configuration, IngredientImpl, Value};
|
||||||
use crate::sync::Arc;
|
use crate::sync::Arc;
|
||||||
use crate::table::memo::MemoTableTypes;
|
use crate::table::memo::MemoTableTypes;
|
||||||
use crate::zalsa::IngredientIndex;
|
use crate::zalsa::IngredientIndex;
|
||||||
use crate::{Database, Id, Revision};
|
use crate::{Id, Revision};
|
||||||
|
|
||||||
/// Ingredient used to represent the fields of a `#[salsa::input]`.
|
/// Ingredient used to represent the fields of a `#[salsa::input]`.
|
||||||
///
|
///
|
||||||
|
@ -52,12 +52,12 @@ where
|
||||||
|
|
||||||
unsafe fn maybe_changed_after(
|
unsafe fn maybe_changed_after(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn Database,
|
zalsa: &crate::zalsa::Zalsa,
|
||||||
|
_db: crate::database::RawDatabase<'_>,
|
||||||
input: Id,
|
input: Id,
|
||||||
revision: Revision,
|
revision: Revision,
|
||||||
_cycle_heads: &mut CycleHeads,
|
_cycle_heads: &mut CycleHeads,
|
||||||
) -> VerifyResult {
|
) -> VerifyResult {
|
||||||
let zalsa = db.zalsa();
|
|
||||||
let value = <IngredientImpl<C>>::data(zalsa, input);
|
let value = <IngredientImpl<C>>::data(zalsa, input);
|
||||||
VerifyResult::changed_if(value.revisions[self.field_index] > revision)
|
VerifyResult::changed_if(value.revisions[self.field_index] > revision)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ use crate::sync::{Arc, Mutex, OnceLock};
|
||||||
use crate::table::memo::{MemoTable, MemoTableTypes, MemoTableWithTypesMut};
|
use crate::table::memo::{MemoTable, MemoTableTypes, MemoTableWithTypesMut};
|
||||||
use crate::table::Slot;
|
use crate::table::Slot;
|
||||||
use crate::zalsa::{IngredientIndex, Zalsa};
|
use crate::zalsa::{IngredientIndex, Zalsa};
|
||||||
use crate::{Database, DatabaseKeyIndex, Event, EventKind, Id, Revision};
|
use crate::{DatabaseKeyIndex, Event, EventKind, Id, Revision};
|
||||||
|
|
||||||
/// Trait that defines the key properties of an interned struct.
|
/// Trait that defines the key properties of an interned struct.
|
||||||
///
|
///
|
||||||
|
@ -296,7 +296,8 @@ where
|
||||||
/// the database ends up trying to intern or allocate a new value.
|
/// the database ends up trying to intern or allocate a new value.
|
||||||
pub fn intern<'db, Key>(
|
pub fn intern<'db, Key>(
|
||||||
&'db self,
|
&'db self,
|
||||||
db: &'db dyn crate::Database,
|
zalsa: &'db Zalsa,
|
||||||
|
zalsa_local: &'db ZalsaLocal,
|
||||||
key: Key,
|
key: Key,
|
||||||
assemble: impl FnOnce(Id, Key) -> C::Fields<'db>,
|
assemble: impl FnOnce(Id, Key) -> C::Fields<'db>,
|
||||||
) -> C::Struct<'db>
|
) -> C::Struct<'db>
|
||||||
|
@ -304,7 +305,7 @@ where
|
||||||
Key: Hash,
|
Key: Hash,
|
||||||
C::Fields<'db>: HashEqLike<Key>,
|
C::Fields<'db>: HashEqLike<Key>,
|
||||||
{
|
{
|
||||||
FromId::from_id(self.intern_id(db, key, assemble))
|
FromId::from_id(self.intern_id(zalsa, zalsa_local, key, assemble))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Intern data to a unique reference.
|
/// Intern data to a unique reference.
|
||||||
|
@ -319,7 +320,8 @@ where
|
||||||
/// the database ends up trying to intern or allocate a new value.
|
/// the database ends up trying to intern or allocate a new value.
|
||||||
pub fn intern_id<'db, Key>(
|
pub fn intern_id<'db, Key>(
|
||||||
&'db self,
|
&'db self,
|
||||||
db: &'db dyn crate::Database,
|
zalsa: &'db Zalsa,
|
||||||
|
zalsa_local: &'db ZalsaLocal,
|
||||||
key: Key,
|
key: Key,
|
||||||
assemble: impl FnOnce(Id, Key) -> C::Fields<'db>,
|
assemble: impl FnOnce(Id, Key) -> C::Fields<'db>,
|
||||||
) -> crate::Id
|
) -> crate::Id
|
||||||
|
@ -331,8 +333,6 @@ where
|
||||||
// so instead we go with this and transmute the lifetime in the `eq` closure
|
// so instead we go with this and transmute the lifetime in the `eq` closure
|
||||||
C::Fields<'db>: HashEqLike<Key>,
|
C::Fields<'db>: HashEqLike<Key>,
|
||||||
{
|
{
|
||||||
let (zalsa, zalsa_local) = db.zalsas();
|
|
||||||
|
|
||||||
// Record the current revision as active.
|
// Record the current revision as active.
|
||||||
let current_revision = zalsa.current_revision();
|
let current_revision = zalsa.current_revision();
|
||||||
self.revision_queue.record(current_revision);
|
self.revision_queue.record(current_revision);
|
||||||
|
@ -735,8 +735,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lookup the data for an interned value based on its ID.
|
/// Lookup the data for an interned value based on its ID.
|
||||||
pub fn data<'db>(&'db self, db: &'db dyn Database, id: Id) -> &'db C::Fields<'db> {
|
pub fn data<'db>(&'db self, zalsa: &'db Zalsa, id: Id) -> &'db C::Fields<'db> {
|
||||||
let zalsa = db.zalsa();
|
|
||||||
let value = zalsa.table().get::<Value<C>>(id);
|
let value = zalsa.table().get::<Value<C>>(id);
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
|
@ -761,12 +760,12 @@ where
|
||||||
/// Lookup the fields from an interned struct.
|
/// Lookup the fields from an interned struct.
|
||||||
///
|
///
|
||||||
/// Note that this is not "leaking" since no dependency edge is required.
|
/// Note that this is not "leaking" since no dependency edge is required.
|
||||||
pub fn fields<'db>(&'db self, db: &'db dyn Database, s: C::Struct<'db>) -> &'db C::Fields<'db> {
|
pub fn fields<'db>(&'db self, zalsa: &'db Zalsa, s: C::Struct<'db>) -> &'db C::Fields<'db> {
|
||||||
self.data(db, AsId::as_id(&s))
|
self.data(zalsa, AsId::as_id(&s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self, db: &mut dyn Database) {
|
pub fn reset(&mut self, zalsa_mut: &mut Zalsa) {
|
||||||
_ = db.zalsa_mut();
|
_ = zalsa_mut;
|
||||||
|
|
||||||
for shard in self.shards.iter() {
|
for shard in self.shards.iter() {
|
||||||
// We can clear the key maps now that we have cancelled all other handles.
|
// We can clear the key maps now that we have cancelled all other handles.
|
||||||
|
@ -776,11 +775,8 @@ where
|
||||||
|
|
||||||
#[cfg(feature = "salsa_unstable")]
|
#[cfg(feature = "salsa_unstable")]
|
||||||
/// Returns all data corresponding to the interned struct.
|
/// Returns all data corresponding to the interned struct.
|
||||||
pub fn entries<'db>(
|
pub fn entries<'db>(&'db self, zalsa: &'db Zalsa) -> impl Iterator<Item = &'db Value<C>> {
|
||||||
&'db self,
|
zalsa.table().slots_of::<Value<C>>()
|
||||||
db: &'db dyn crate::Database,
|
|
||||||
) -> impl Iterator<Item = &'db Value<C>> {
|
|
||||||
db.zalsa().table().slots_of::<Value<C>>()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -798,13 +794,12 @@ where
|
||||||
|
|
||||||
unsafe fn maybe_changed_after(
|
unsafe fn maybe_changed_after(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn Database,
|
zalsa: &crate::zalsa::Zalsa,
|
||||||
|
_db: crate::database::RawDatabase<'_>,
|
||||||
input: Id,
|
input: Id,
|
||||||
_revision: Revision,
|
_revision: Revision,
|
||||||
_cycle_heads: &mut CycleHeads,
|
_cycle_heads: &mut CycleHeads,
|
||||||
) -> VerifyResult {
|
) -> VerifyResult {
|
||||||
let zalsa = db.zalsa();
|
|
||||||
|
|
||||||
// Record the current revision as active.
|
// Record the current revision as active.
|
||||||
let current_revision = zalsa.current_revision();
|
let current_revision = zalsa.current_revision();
|
||||||
self.revision_queue.record(current_revision);
|
self.revision_queue.record(current_revision);
|
||||||
|
@ -852,7 +847,7 @@ where
|
||||||
|
|
||||||
/// Returns memory usage information about any interned values.
|
/// Returns memory usage information about any interned values.
|
||||||
#[cfg(all(not(feature = "shuttle"), feature = "salsa_unstable"))]
|
#[cfg(all(not(feature = "shuttle"), feature = "salsa_unstable"))]
|
||||||
fn memory_usage(&self, db: &dyn Database) -> Option<Vec<crate::database::SlotInfo>> {
|
fn memory_usage(&self, db: &dyn crate::Database) -> Option<Vec<crate::database::SlotInfo>> {
|
||||||
use parking_lot::lock_api::RawMutex;
|
use parking_lot::lock_api::RawMutex;
|
||||||
|
|
||||||
for shard in self.shards.iter() {
|
for shard in self.shards.iter() {
|
||||||
|
@ -861,7 +856,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
let memory_usage = self
|
let memory_usage = self
|
||||||
.entries(db)
|
.entries(db.zalsa())
|
||||||
// SAFETY: The memo table belongs to a value that we allocated, so it
|
// SAFETY: The memo table belongs to a value that we allocated, so it
|
||||||
// has the correct type. Additionally, we are holding the locks for all shards.
|
// has the correct type. Additionally, we are holding the locks for all shards.
|
||||||
.map(|value| unsafe { value.memory_usage(&self.memo_table_types) })
|
.map(|value| unsafe { value.memory_usage(&self.memo_table_types) })
|
||||||
|
|
|
@ -3,7 +3,7 @@ use core::fmt;
|
||||||
use crate::cycle::CycleHeads;
|
use crate::cycle::CycleHeads;
|
||||||
use crate::function::VerifyResult;
|
use crate::function::VerifyResult;
|
||||||
use crate::zalsa::{IngredientIndex, Zalsa};
|
use crate::zalsa::{IngredientIndex, Zalsa};
|
||||||
use crate::{Database, Id};
|
use crate::Id;
|
||||||
|
|
||||||
// ANCHOR: DatabaseKeyIndex
|
// ANCHOR: DatabaseKeyIndex
|
||||||
/// An integer that uniquely identifies a particular query instance within the
|
/// An integer that uniquely identifies a particular query instance within the
|
||||||
|
@ -36,16 +36,18 @@ impl DatabaseKeyIndex {
|
||||||
|
|
||||||
pub(crate) fn maybe_changed_after(
|
pub(crate) fn maybe_changed_after(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn Database,
|
db: crate::database::RawDatabase<'_>,
|
||||||
zalsa: &Zalsa,
|
zalsa: &Zalsa,
|
||||||
last_verified_at: crate::Revision,
|
last_verified_at: crate::Revision,
|
||||||
cycle_heads: &mut CycleHeads,
|
cycle_heads: &mut CycleHeads,
|
||||||
) -> VerifyResult {
|
) -> VerifyResult {
|
||||||
// SAFETY: The `db` belongs to the ingredient
|
// SAFETY: The `db` belongs to the ingredient
|
||||||
unsafe {
|
unsafe {
|
||||||
|
// here, `db` has to be either the correct type already, or a subtype (as far as trait
|
||||||
|
// hierarchy is concerned)
|
||||||
zalsa
|
zalsa
|
||||||
.lookup_ingredient(self.ingredient_index())
|
.lookup_ingredient(self.ingredient_index())
|
||||||
.maybe_changed_after(db, self.key_index(), last_verified_at, cycle_heads)
|
.maybe_changed_after(zalsa, db, self.key_index(), last_verified_at, cycle_heads)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ pub use self::accumulator::Accumulator;
|
||||||
pub use self::active_query::Backtrace;
|
pub use self::active_query::Backtrace;
|
||||||
pub use self::cancelled::Cancelled;
|
pub use self::cancelled::Cancelled;
|
||||||
pub use self::cycle::CycleRecoveryAction;
|
pub use self::cycle::CycleRecoveryAction;
|
||||||
pub use self::database::{AsDynDatabase, Database};
|
pub use self::database::Database;
|
||||||
pub use self::database_impl::DatabaseImpl;
|
pub use self::database_impl::DatabaseImpl;
|
||||||
pub use self::durability::Durability;
|
pub use self::durability::Durability;
|
||||||
pub use self::event::{Event, EventKind};
|
pub use self::event::{Event, EventKind};
|
||||||
|
|
|
@ -1,44 +1,91 @@
|
||||||
use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelIterator};
|
||||||
|
|
||||||
use crate::Database;
|
use crate::{database::RawDatabase, views::DatabaseDownCaster, Database};
|
||||||
|
|
||||||
pub fn par_map<Db, F, T, R, C>(db: &Db, inputs: impl IntoParallelIterator<Item = T>, op: F) -> C
|
pub fn par_map<Db, F, T, R, C>(db: &Db, inputs: impl IntoParallelIterator<Item = T>, op: F) -> C
|
||||||
where
|
where
|
||||||
Db: Database + ?Sized,
|
Db: Database + ?Sized + Send,
|
||||||
F: Fn(&Db, T) -> R + Sync + Send,
|
F: Fn(&Db, T) -> R + Sync + Send,
|
||||||
T: Send,
|
T: Send,
|
||||||
R: Send + Sync,
|
R: Send + Sync,
|
||||||
C: FromParallelIterator<R>,
|
C: FromParallelIterator<R>,
|
||||||
{
|
{
|
||||||
|
let views = db.zalsa().views();
|
||||||
|
let caster = &views.downcaster_for::<Db>();
|
||||||
|
let db_caster = &views.downcaster_for::<dyn Database>();
|
||||||
inputs
|
inputs
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.map_with(DbForkOnClone(db.fork_db()), |db, element| {
|
.map_with(
|
||||||
op(db.0.as_view(), element)
|
DbForkOnClone(db.fork_db(), caster, db_caster),
|
||||||
})
|
|db, element| op(db.as_view(), element),
|
||||||
|
)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DbForkOnClone(Box<dyn Database>);
|
struct DbForkOnClone<'views, Db: Database + ?Sized>(
|
||||||
|
RawDatabase<'static>,
|
||||||
|
&'views DatabaseDownCaster<Db>,
|
||||||
|
&'views DatabaseDownCaster<dyn Database>,
|
||||||
|
);
|
||||||
|
|
||||||
impl Clone for DbForkOnClone {
|
// SAFETY: `T: Send` -> `&own T: Send`, `DbForkOnClone` is an owning pointer
|
||||||
fn clone(&self) -> Self {
|
unsafe impl<Db: Send + Database + ?Sized> Send for DbForkOnClone<'_, Db> {}
|
||||||
DbForkOnClone(self.0.fork_db())
|
|
||||||
|
impl<Db: Database + ?Sized> DbForkOnClone<'_, Db> {
|
||||||
|
fn as_view(&self) -> &Db {
|
||||||
|
// SAFETY: The downcaster ensures that the pointer is valid for the lifetime of the view.
|
||||||
|
unsafe { self.1.downcast_unchecked(self.0) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn join<A, B, RA, RB, Db: Database + ?Sized>(db: &Db, a: A, b: B) -> (RA, RB)
|
impl<Db: Database + ?Sized> Drop for DbForkOnClone<'_, Db> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// SAFETY: `caster` is derived from a `db` fitting for our database clone
|
||||||
|
let db = unsafe { self.1.downcast_mut_unchecked(self.0) };
|
||||||
|
// SAFETY: `db` has been box allocated and leaked by `fork_db`
|
||||||
|
_ = unsafe { Box::from_raw(db) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Db: Database + ?Sized> Clone for DbForkOnClone<'_, Db> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
DbForkOnClone(
|
||||||
|
// SAFETY: `caster` is derived from a `db` fitting for our database clone
|
||||||
|
unsafe { self.2.downcast_unchecked(self.0) }.fork_db(),
|
||||||
|
self.1,
|
||||||
|
self.2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn join<A, B, RA, RB, Db: Send + Database + ?Sized>(db: &Db, a: A, b: B) -> (RA, RB)
|
||||||
where
|
where
|
||||||
A: FnOnce(&Db) -> RA + Send,
|
A: FnOnce(&Db) -> RA + Send,
|
||||||
B: FnOnce(&Db) -> RB + Send,
|
B: FnOnce(&Db) -> RB + Send,
|
||||||
RA: Send,
|
RA: Send,
|
||||||
RB: Send,
|
RB: Send,
|
||||||
{
|
{
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct AssertSend<T>(T);
|
||||||
|
// SAFETY: We send owning pointers over, which are Send, given the `Db` type parameter above is Send
|
||||||
|
unsafe impl<T> Send for AssertSend<T> {}
|
||||||
|
|
||||||
|
let caster = &db.zalsa().views().downcaster_for::<Db>();
|
||||||
// we need to fork eagerly, as `rayon::join_context` gives us no option to tell whether we get
|
// we need to fork eagerly, as `rayon::join_context` gives us no option to tell whether we get
|
||||||
// moved to another thread before the closure is executed
|
// moved to another thread before the closure is executed
|
||||||
let db_a = db.fork_db();
|
let db_a = AssertSend(db.fork_db());
|
||||||
let db_b = db.fork_db();
|
let db_b = AssertSend(db.fork_db());
|
||||||
rayon::join(
|
let res = rayon::join(
|
||||||
move || a(db_a.as_view::<Db>()),
|
// SAFETY: `caster` is derived from a `db` fitting for our database clone
|
||||||
move || b(db_b.as_view::<Db>()),
|
move || a(unsafe { caster.downcast_unchecked({ db_a }.0) }),
|
||||||
)
|
// SAFETY: `caster` is derived from a `db` fitting for our database clone
|
||||||
|
move || b(unsafe { caster.downcast_unchecked({ db_b }.0) }),
|
||||||
|
);
|
||||||
|
|
||||||
|
// SAFETY: `db` has been box allocated and leaked by `fork_db`
|
||||||
|
// FIXME: Clean this mess up, RAII
|
||||||
|
_ = unsafe { Box::from_raw(caster.downcast_mut_unchecked(db_a.0)) };
|
||||||
|
// SAFETY: `db` has been box allocated and leaked by `fork_db`
|
||||||
|
_ = unsafe { Box::from_raw(caster.downcast_mut_unchecked(db_b.0)) };
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::panic::RefUnwindSafe;
|
use std::panic::RefUnwindSafe;
|
||||||
|
|
||||||
|
use crate::database::RawDatabase;
|
||||||
use crate::sync::{Arc, Condvar, Mutex};
|
use crate::sync::{Arc, Condvar, Mutex};
|
||||||
use crate::zalsa::{ErasedJar, HasJar, Zalsa, ZalsaDatabase};
|
use crate::zalsa::{ErasedJar, HasJar, Zalsa, ZalsaDatabase};
|
||||||
use crate::zalsa_local::{self, ZalsaLocal};
|
use crate::zalsa_local::{self, ZalsaLocal};
|
||||||
|
@ -245,8 +246,8 @@ unsafe impl<T: HasStorage> ZalsaDatabase for T {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn fork_db(&self) -> Box<dyn Database> {
|
fn fork_db(&self) -> RawDatabase<'static> {
|
||||||
Box::new(self.clone())
|
Box::leak(Box::new(self.clone())).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ use crate::sync::Arc;
|
||||||
use crate::table::memo::{MemoTable, MemoTableTypes, MemoTableWithTypesMut};
|
use crate::table::memo::{MemoTable, MemoTableTypes, MemoTableWithTypesMut};
|
||||||
use crate::table::{Slot, Table};
|
use crate::table::{Slot, Table};
|
||||||
use crate::zalsa::{IngredientIndex, Zalsa};
|
use crate::zalsa::{IngredientIndex, Zalsa};
|
||||||
use crate::{Database, Durability, Event, EventKind, Id, Revision};
|
use crate::{Durability, Event, EventKind, Id, Revision};
|
||||||
|
|
||||||
pub mod tracked_field;
|
pub mod tracked_field;
|
||||||
|
|
||||||
|
@ -375,11 +375,10 @@ where
|
||||||
|
|
||||||
pub fn new_struct<'db>(
|
pub fn new_struct<'db>(
|
||||||
&'db self,
|
&'db self,
|
||||||
db: &'db dyn Database,
|
zalsa: &'db Zalsa,
|
||||||
|
zalsa_local: &'db ZalsaLocal,
|
||||||
mut fields: C::Fields<'db>,
|
mut fields: C::Fields<'db>,
|
||||||
) -> C::Struct<'db> {
|
) -> C::Struct<'db> {
|
||||||
let (zalsa, zalsa_local) = db.zalsas();
|
|
||||||
|
|
||||||
let identity_hash = IdentityHash {
|
let identity_hash = IdentityHash {
|
||||||
ingredient_index: self.ingredient_index,
|
ingredient_index: self.ingredient_index,
|
||||||
hash: crate::hash::hash(&C::untracked_fields(&fields)),
|
hash: crate::hash::hash(&C::untracked_fields(&fields)),
|
||||||
|
@ -734,11 +733,11 @@ where
|
||||||
/// Used for debugging.
|
/// Used for debugging.
|
||||||
pub fn leak_fields<'db>(
|
pub fn leak_fields<'db>(
|
||||||
&'db self,
|
&'db self,
|
||||||
db: &'db dyn Database,
|
zalsa: &'db Zalsa,
|
||||||
s: C::Struct<'db>,
|
s: C::Struct<'db>,
|
||||||
) -> &'db C::Fields<'db> {
|
) -> &'db C::Fields<'db> {
|
||||||
let id = AsId::as_id(&s);
|
let id = AsId::as_id(&s);
|
||||||
let data = Self::data(db.zalsa().table(), id);
|
let data = Self::data(zalsa.table(), id);
|
||||||
data.fields()
|
data.fields()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -748,11 +747,11 @@ where
|
||||||
/// The caller is responsible for selecting the appropriate element.
|
/// The caller is responsible for selecting the appropriate element.
|
||||||
pub fn tracked_field<'db>(
|
pub fn tracked_field<'db>(
|
||||||
&'db self,
|
&'db self,
|
||||||
db: &'db dyn crate::Database,
|
zalsa: &'db Zalsa,
|
||||||
|
zalsa_local: &'db ZalsaLocal,
|
||||||
s: C::Struct<'db>,
|
s: C::Struct<'db>,
|
||||||
relative_tracked_index: usize,
|
relative_tracked_index: usize,
|
||||||
) -> &'db C::Fields<'db> {
|
) -> &'db C::Fields<'db> {
|
||||||
let (zalsa, zalsa_local) = db.zalsas();
|
|
||||||
let id = AsId::as_id(&s);
|
let id = AsId::as_id(&s);
|
||||||
let field_ingredient_index = self.ingredient_index.successor(relative_tracked_index);
|
let field_ingredient_index = self.ingredient_index.successor(relative_tracked_index);
|
||||||
let data = Self::data(zalsa.table(), id);
|
let data = Self::data(zalsa.table(), id);
|
||||||
|
@ -776,10 +775,9 @@ where
|
||||||
/// The caller is responsible for selecting the appropriate element.
|
/// The caller is responsible for selecting the appropriate element.
|
||||||
pub fn untracked_field<'db>(
|
pub fn untracked_field<'db>(
|
||||||
&'db self,
|
&'db self,
|
||||||
db: &'db dyn crate::Database,
|
zalsa: &'db Zalsa,
|
||||||
s: C::Struct<'db>,
|
s: C::Struct<'db>,
|
||||||
) -> &'db C::Fields<'db> {
|
) -> &'db C::Fields<'db> {
|
||||||
let zalsa = db.zalsa();
|
|
||||||
let id = AsId::as_id(&s);
|
let id = AsId::as_id(&s);
|
||||||
let data = Self::data(zalsa.table(), id);
|
let data = Self::data(zalsa.table(), id);
|
||||||
|
|
||||||
|
@ -794,11 +792,8 @@ where
|
||||||
|
|
||||||
#[cfg(feature = "salsa_unstable")]
|
#[cfg(feature = "salsa_unstable")]
|
||||||
/// Returns all data corresponding to the tracked struct.
|
/// Returns all data corresponding to the tracked struct.
|
||||||
pub fn entries<'db>(
|
pub fn entries<'db>(&'db self, zalsa: &'db Zalsa) -> impl Iterator<Item = &'db Value<C>> {
|
||||||
&'db self,
|
zalsa.table().slots_of::<Value<C>>()
|
||||||
db: &'db dyn crate::Database,
|
|
||||||
) -> impl Iterator<Item = &'db Value<C>> {
|
|
||||||
db.zalsa().table().slots_of::<Value<C>>()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -816,7 +811,8 @@ where
|
||||||
|
|
||||||
unsafe fn maybe_changed_after(
|
unsafe fn maybe_changed_after(
|
||||||
&self,
|
&self,
|
||||||
_db: &dyn Database,
|
_zalsa: &crate::zalsa::Zalsa,
|
||||||
|
_db: crate::database::RawDatabase<'_>,
|
||||||
_input: Id,
|
_input: Id,
|
||||||
_revision: Revision,
|
_revision: Revision,
|
||||||
_cycle_heads: &mut CycleHeads,
|
_cycle_heads: &mut CycleHeads,
|
||||||
|
@ -863,9 +859,9 @@ where
|
||||||
|
|
||||||
/// Returns memory usage information about any tracked structs.
|
/// Returns memory usage information about any tracked structs.
|
||||||
#[cfg(feature = "salsa_unstable")]
|
#[cfg(feature = "salsa_unstable")]
|
||||||
fn memory_usage(&self, db: &dyn Database) -> Option<Vec<crate::database::SlotInfo>> {
|
fn memory_usage(&self, db: &dyn crate::Database) -> Option<Vec<crate::database::SlotInfo>> {
|
||||||
let memory_usage = self
|
let memory_usage = self
|
||||||
.entries(db)
|
.entries(db.zalsa())
|
||||||
// SAFETY: The memo table belongs to a value that we allocated, so it
|
// SAFETY: The memo table belongs to a value that we allocated, so it
|
||||||
// has the correct type.
|
// has the correct type.
|
||||||
.map(|value| unsafe { value.memory_usage(&self.memo_table_types) })
|
.map(|value| unsafe { value.memory_usage(&self.memo_table_types) })
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::sync::Arc;
|
||||||
use crate::table::memo::MemoTableTypes;
|
use crate::table::memo::MemoTableTypes;
|
||||||
use crate::tracked_struct::{Configuration, Value};
|
use crate::tracked_struct::{Configuration, Value};
|
||||||
use crate::zalsa::IngredientIndex;
|
use crate::zalsa::IngredientIndex;
|
||||||
use crate::{Database, Id};
|
use crate::Id;
|
||||||
|
|
||||||
/// Created for each tracked struct.
|
/// Created for each tracked struct.
|
||||||
///
|
///
|
||||||
|
@ -55,14 +55,14 @@ where
|
||||||
self.ingredient_index
|
self.ingredient_index
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn maybe_changed_after<'db>(
|
unsafe fn maybe_changed_after(
|
||||||
&'db self,
|
&self,
|
||||||
db: &'db dyn Database,
|
zalsa: &crate::zalsa::Zalsa,
|
||||||
|
_db: crate::database::RawDatabase<'_>,
|
||||||
input: Id,
|
input: Id,
|
||||||
revision: crate::Revision,
|
revision: crate::Revision,
|
||||||
_cycle_heads: &mut CycleHeads,
|
_cycle_heads: &mut CycleHeads,
|
||||||
) -> VerifyResult {
|
) -> VerifyResult {
|
||||||
let zalsa = db.zalsa();
|
|
||||||
let data = <super::IngredientImpl<C>>::data(zalsa.table(), input);
|
let data = <super::IngredientImpl<C>>::data(zalsa.table(), input);
|
||||||
let field_changed_at = data.revisions[self.field_index];
|
let field_changed_at = data.revisions[self.field_index];
|
||||||
VerifyResult::changed_if(field_changed_at > revision)
|
VerifyResult::changed_if(field_changed_at > revision)
|
||||||
|
|
138
src/views.rs
138
src/views.rs
|
@ -1,10 +1,15 @@
|
||||||
use std::any::{Any, TypeId};
|
use std::{
|
||||||
|
any::{Any, TypeId},
|
||||||
|
marker::PhantomData,
|
||||||
|
mem,
|
||||||
|
ptr::NonNull,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::Database;
|
use crate::{database::RawDatabase, Database};
|
||||||
|
|
||||||
/// A `Views` struct is associated with some specific database type
|
/// A `Views` struct is associated with some specific database type
|
||||||
/// (a `DatabaseImpl<U>` for some existential `U`). It contains functions
|
/// (a `DatabaseImpl<U>` for some existential `U`). It contains functions
|
||||||
/// to downcast from `dyn Database` to `dyn DbView` for various traits `DbView` via this specific
|
/// to downcast to `dyn DbView` for various traits `DbView` via this specific
|
||||||
/// database type.
|
/// database type.
|
||||||
/// None of these types are known at compilation time, they are all checked
|
/// None of these types are known at compilation time, they are all checked
|
||||||
/// dynamically through `TypeId` magic.
|
/// dynamically through `TypeId` magic.
|
||||||
|
@ -13,6 +18,7 @@ pub struct Views {
|
||||||
view_casters: boxcar::Vec<ViewCaster>,
|
view_casters: boxcar::Vec<ViewCaster>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
struct ViewCaster {
|
struct ViewCaster {
|
||||||
/// The id of the target type `dyn DbView` that we can cast to.
|
/// The id of the target type `dyn DbView` that we can cast to.
|
||||||
target_type_id: TypeId,
|
target_type_id: TypeId,
|
||||||
|
@ -20,50 +26,69 @@ struct ViewCaster {
|
||||||
/// The name of the target type `dyn DbView` that we can cast to.
|
/// The name of the target type `dyn DbView` that we can cast to.
|
||||||
type_name: &'static str,
|
type_name: &'static str,
|
||||||
|
|
||||||
/// Type-erased function pointer that downcasts from `dyn Database` to `dyn DbView`.
|
/// Type-erased function pointer that downcasts to `dyn DbView`.
|
||||||
cast: ErasedDatabaseDownCasterSig,
|
cast: ErasedDatabaseDownCasterSig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ViewCaster {
|
impl ViewCaster {
|
||||||
fn new<DbView: ?Sized + Any>(func: unsafe fn(&dyn Database) -> &DbView) -> ViewCaster {
|
fn new<DbView: ?Sized + Any>(func: DatabaseDownCasterSig<DbView>) -> ViewCaster {
|
||||||
ViewCaster {
|
ViewCaster {
|
||||||
target_type_id: TypeId::of::<DbView>(),
|
target_type_id: TypeId::of::<DbView>(),
|
||||||
type_name: std::any::type_name::<DbView>(),
|
type_name: std::any::type_name::<DbView>(),
|
||||||
// SAFETY: We are type erasing for storage, taking care of unerasing before we call
|
// SAFETY: We are type erasing for storage, taking care of unerasing before we call
|
||||||
// the function pointer.
|
// the function pointer.
|
||||||
cast: unsafe {
|
cast: unsafe {
|
||||||
std::mem::transmute::<DatabaseDownCasterSig<DbView>, ErasedDatabaseDownCasterSig>(
|
mem::transmute::<DatabaseDownCasterSig<DbView>, ErasedDatabaseDownCasterSig>(func)
|
||||||
func,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ErasedDatabaseDownCasterSig = unsafe fn(&dyn Database) -> *const ();
|
type ErasedDatabaseDownCasterSig = unsafe fn(RawDatabase<'_>) -> NonNull<()>;
|
||||||
type DatabaseDownCasterSig<DbView> = unsafe fn(&dyn Database) -> &DbView;
|
type DatabaseDownCasterSig<DbView> = unsafe fn(RawDatabase<'_>) -> NonNull<DbView>;
|
||||||
|
|
||||||
pub struct DatabaseDownCaster<DbView: ?Sized>(TypeId, DatabaseDownCasterSig<DbView>);
|
#[repr(transparent)]
|
||||||
|
pub struct DatabaseDownCaster<DbView: ?Sized>(ViewCaster, PhantomData<fn() -> DbView>);
|
||||||
|
|
||||||
|
impl<DbView: ?Sized> Copy for DatabaseDownCaster<DbView> {}
|
||||||
|
impl<DbView: ?Sized> Clone for DatabaseDownCaster<DbView> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<DbView: ?Sized + Any> DatabaseDownCaster<DbView> {
|
impl<DbView: ?Sized + Any> DatabaseDownCaster<DbView> {
|
||||||
pub fn downcast<'db>(&self, db: &'db dyn Database) -> &'db DbView {
|
|
||||||
assert_eq!(
|
|
||||||
self.0,
|
|
||||||
db.type_id(),
|
|
||||||
"Database type does not match the expected type for this `Views` instance"
|
|
||||||
);
|
|
||||||
// SAFETY: We've asserted that the database is correct.
|
|
||||||
unsafe { (self.1)(db) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Downcast `db` to `DbView`.
|
/// Downcast `db` to `DbView`.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The caller must ensure that `db` is of the correct type.
|
/// The caller must ensure that `db` is of the correct type.
|
||||||
pub unsafe fn downcast_unchecked<'db>(&self, db: &'db dyn Database) -> &'db DbView {
|
#[inline]
|
||||||
|
pub unsafe fn downcast_unchecked<'db>(&self, db: RawDatabase<'db>) -> &'db DbView {
|
||||||
// SAFETY: The caller must ensure that `db` is of the correct type.
|
// SAFETY: The caller must ensure that `db` is of the correct type.
|
||||||
unsafe { (self.1)(db) }
|
// The returned pointer is live for `'db` due to construction of the downcaster functions.
|
||||||
|
unsafe { (self.unerased_downcaster())(db).as_ref() }
|
||||||
|
}
|
||||||
|
/// Downcast `db` to `DbView`.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller must ensure that `db` is of the correct type.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn downcast_mut_unchecked<'db>(&self, db: RawDatabase<'db>) -> &'db mut DbView {
|
||||||
|
// SAFETY: The caller must ensure that `db` is of the correct type.
|
||||||
|
// The returned pointer is live for `'db` due to construction of the downcaster functions.
|
||||||
|
unsafe { (self.unerased_downcaster())(db).as_mut() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn unerased_downcaster(&self) -> DatabaseDownCasterSig<DbView> {
|
||||||
|
// SAFETY: The type-erased function pointer is guaranteed to be ABI compatible for `DbView`
|
||||||
|
unsafe {
|
||||||
|
mem::transmute::<ErasedDatabaseDownCasterSig, DatabaseDownCasterSig<DbView>>(
|
||||||
|
self.0.cast,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,58 +96,63 @@ impl Views {
|
||||||
pub(crate) fn new<Db: Database>() -> Self {
|
pub(crate) fn new<Db: Database>() -> Self {
|
||||||
let source_type_id = TypeId::of::<Db>();
|
let source_type_id = TypeId::of::<Db>();
|
||||||
let view_casters = boxcar::Vec::new();
|
let view_casters = boxcar::Vec::new();
|
||||||
// special case the no-op transformation, that way we skip out on reconstructing the wide pointer
|
view_casters.push(ViewCaster::new::<dyn Database>(|db| db.ptr.cast::<Db>()));
|
||||||
view_casters.push(ViewCaster::new::<dyn Database>(|db| db));
|
|
||||||
Self {
|
Self {
|
||||||
source_type_id,
|
source_type_id,
|
||||||
view_casters,
|
view_casters,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a new downcaster from `dyn Database` to `dyn DbView`.
|
/// Add a new downcaster to `dyn DbView`.
|
||||||
pub fn add<DbView: ?Sized + Any>(
|
pub fn add<Concrete: 'static, DbView: ?Sized + Any>(
|
||||||
&self,
|
&self,
|
||||||
func: DatabaseDownCasterSig<DbView>,
|
func: fn(NonNull<Concrete>) -> NonNull<DbView>,
|
||||||
) -> DatabaseDownCaster<DbView> {
|
) -> &DatabaseDownCaster<DbView> {
|
||||||
if let Some(view) = self.try_downcaster_for() {
|
assert_eq!(self.source_type_id, TypeId::of::<Concrete>());
|
||||||
return view;
|
let target_type_id = TypeId::of::<DbView>();
|
||||||
|
if let Some((_, caster)) = self
|
||||||
|
.view_casters
|
||||||
|
.iter()
|
||||||
|
.find(|(_, u)| u.target_type_id == target_type_id)
|
||||||
|
{
|
||||||
|
// SAFETY: The type-erased function pointer is guaranteed to be valid for `DbView`
|
||||||
|
return unsafe { &*(&raw const *caster).cast::<DatabaseDownCaster<DbView>>() };
|
||||||
}
|
}
|
||||||
|
|
||||||
self.view_casters.push(ViewCaster::new::<DbView>(func));
|
// SAFETY: We are type erasing the function pointer for storage, and we will unerase it
|
||||||
DatabaseDownCaster(self.source_type_id, func)
|
// before we call it.
|
||||||
|
let caster = unsafe {
|
||||||
|
mem::transmute::<fn(NonNull<Concrete>) -> NonNull<DbView>, DatabaseDownCasterSig<DbView>>(
|
||||||
|
func,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let caster = ViewCaster::new::<DbView>(caster);
|
||||||
|
let idx = self.view_casters.push(caster);
|
||||||
|
// SAFETY: The type-erased function pointer is guaranteed to be valid for `DbView`
|
||||||
|
unsafe { &*(&raw const self.view_casters[idx]).cast::<DatabaseDownCaster<DbView>>() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve an downcaster function from `dyn Database` to `dyn DbView`.
|
/// Retrieve an downcaster function to `dyn DbView`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// If the underlying type of `db` is not the same as the database type this upcasts was created for.
|
/// If the underlying type of `db` is not the same as the database type this downcasts was created for.
|
||||||
pub fn downcaster_for<DbView: ?Sized + Any>(&self) -> DatabaseDownCaster<DbView> {
|
pub fn downcaster_for<DbView: ?Sized + Any>(&self) -> &DatabaseDownCaster<DbView> {
|
||||||
self.try_downcaster_for().unwrap_or_else(|| {
|
|
||||||
panic!(
|
|
||||||
"No downcaster registered for type `{}` in `Views`",
|
|
||||||
std::any::type_name::<DbView>(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve an downcaster function from `dyn Database` to `dyn DbView`, if it exists.
|
|
||||||
#[inline]
|
|
||||||
pub fn try_downcaster_for<DbView: ?Sized + Any>(&self) -> Option<DatabaseDownCaster<DbView>> {
|
|
||||||
let view_type_id = TypeId::of::<DbView>();
|
let view_type_id = TypeId::of::<DbView>();
|
||||||
for (_, view) in self.view_casters.iter() {
|
for (_, view) in self.view_casters.iter() {
|
||||||
if view.target_type_id == view_type_id {
|
if view.target_type_id == view_type_id {
|
||||||
// SAFETY: We are unerasing the type erased function pointer having made sure the
|
// SAFETY: We are unerasing the type erased function pointer having made sure the
|
||||||
// `TypeId` matches.
|
// TypeId matches.
|
||||||
return Some(DatabaseDownCaster(self.source_type_id, unsafe {
|
return unsafe {
|
||||||
std::mem::transmute::<ErasedDatabaseDownCasterSig, DatabaseDownCasterSig<DbView>>(
|
&*((view as *const ViewCaster).cast::<DatabaseDownCaster<DbView>>())
|
||||||
view.cast,
|
};
|
||||||
)
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
panic!(
|
||||||
|
"No downcaster registered for type `{}` in `Views`",
|
||||||
|
std::any::type_name::<DbView>(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ use std::panic::RefUnwindSafe;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
|
use crate::database::RawDatabase;
|
||||||
use crate::hash::TypeIdHasher;
|
use crate::hash::TypeIdHasher;
|
||||||
use crate::ingredient::{Ingredient, Jar};
|
use crate::ingredient::{Ingredient, Jar};
|
||||||
use crate::plumbing::SalsaStructInDb;
|
use crate::plumbing::SalsaStructInDb;
|
||||||
|
@ -52,7 +53,7 @@ pub unsafe trait ZalsaDatabase: Any {
|
||||||
|
|
||||||
/// Clone the database.
|
/// Clone the database.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn fork_db(&self) -> Box<dyn Database>;
|
fn fork_db(&self) -> RawDatabase<'static>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn views<Db: ?Sized + Database>(db: &Db) -> &Views {
|
pub fn views<Db: ?Sized + Database>(db: &Db) -> &Views {
|
||||||
|
|
|
@ -22,14 +22,15 @@ fn tracked_fn(db: &dyn salsa::Database, input: InputStruct) -> TrackedStruct<'_>
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute() {
|
fn execute() {
|
||||||
|
use salsa::plumbing::ZalsaDatabase;
|
||||||
let db = salsa::DatabaseImpl::new();
|
let db = salsa::DatabaseImpl::new();
|
||||||
|
|
||||||
let _ = InternedStruct::new(&db, "Salsa".to_string());
|
let _ = InternedStruct::new(&db, "Salsa".to_string());
|
||||||
let _ = InternedStruct::new(&db, "Salsa2".to_string());
|
let _ = InternedStruct::new(&db, "Salsa2".to_string());
|
||||||
|
|
||||||
// test interned structs
|
// test interned structs
|
||||||
let interned = InternedStruct::ingredient(&db)
|
let interned = InternedStruct::ingredient(db.zalsa())
|
||||||
.entries(&db)
|
.entries(db.zalsa())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(interned.len(), 2);
|
assert_eq!(interned.len(), 2);
|
||||||
|
@ -40,7 +41,7 @@ fn execute() {
|
||||||
let input = InputStruct::new(&db, 22);
|
let input = InputStruct::new(&db, 22);
|
||||||
|
|
||||||
let inputs = InputStruct::ingredient(&db)
|
let inputs = InputStruct::ingredient(&db)
|
||||||
.entries(&db)
|
.entries(db.zalsa())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(inputs.len(), 1);
|
assert_eq!(inputs.len(), 1);
|
||||||
|
@ -50,7 +51,7 @@ fn execute() {
|
||||||
let computed = tracked_fn(&db, input).field(&db);
|
let computed = tracked_fn(&db, input).field(&db);
|
||||||
assert_eq!(computed, 44);
|
assert_eq!(computed, 44);
|
||||||
let tracked = TrackedStruct::ingredient(&db)
|
let tracked = TrackedStruct::ingredient(&db)
|
||||||
.entries(&db)
|
.entries(db.zalsa())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(tracked.len(), 1);
|
assert_eq!(tracked.len(), 1);
|
||||||
|
|
|
@ -132,13 +132,13 @@ fn interning_boxed() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interned_structs_have_public_ingredients() {
|
fn interned_structs_have_public_ingredients() {
|
||||||
use salsa::plumbing::AsId;
|
use salsa::plumbing::{AsId, ZalsaDatabase};
|
||||||
|
|
||||||
let db = salsa::DatabaseImpl::new();
|
let db = salsa::DatabaseImpl::new();
|
||||||
let s = InternedString::new(&db, String::from("Hello, world!"));
|
let s = InternedString::new(&db, String::from("Hello, world!"));
|
||||||
let underlying_id = s.0;
|
let underlying_id = s.0;
|
||||||
|
|
||||||
let data = InternedString::ingredient(&db).data(&db, underlying_id.as_id());
|
let data = InternedString::ingredient(db.zalsa()).data(db.zalsa(), underlying_id.as_id());
|
||||||
assert_eq!(data, &(String::from("Hello, world!"),));
|
assert_eq!(data, &(String::from("Hello, world!"),));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -181,7 +181,8 @@ const _: () = {
|
||||||
String: zalsa_::interned::HashEqLike<T0>,
|
String: zalsa_::interned::HashEqLike<T0>,
|
||||||
{
|
{
|
||||||
Configuration_::ingredient(db).intern(
|
Configuration_::ingredient(db).intern(
|
||||||
db.as_dyn_database(),
|
db.zalsa(),
|
||||||
|
db.zalsa_local(),
|
||||||
StructKey::<'db>(data, std::marker::PhantomData::default()),
|
StructKey::<'db>(data, std::marker::PhantomData::default()),
|
||||||
|id, data| {
|
|id, data| {
|
||||||
StructData(
|
StructData(
|
||||||
|
@ -195,20 +196,20 @@ const _: () = {
|
||||||
where
|
where
|
||||||
Db_: ?Sized + zalsa_::Database,
|
Db_: ?Sized + zalsa_::Database,
|
||||||
{
|
{
|
||||||
let fields = Configuration_::ingredient(db).fields(db.as_dyn_database(), self);
|
let fields = Configuration_::ingredient(db).fields(db.zalsa(), self);
|
||||||
std::clone::Clone::clone((&fields.0))
|
std::clone::Clone::clone((&fields.0))
|
||||||
}
|
}
|
||||||
fn other<Db_>(self, db: &'db Db_) -> InternedString<'db>
|
fn other<Db_>(self, db: &'db Db_) -> InternedString<'db>
|
||||||
where
|
where
|
||||||
Db_: ?Sized + zalsa_::Database,
|
Db_: ?Sized + zalsa_::Database,
|
||||||
{
|
{
|
||||||
let fields = Configuration_::ingredient(db).fields(db.as_dyn_database(), self);
|
let fields = Configuration_::ingredient(db).fields(db.zalsa(), self);
|
||||||
std::clone::Clone::clone((&fields.1))
|
std::clone::Clone::clone((&fields.1))
|
||||||
}
|
}
|
||||||
#[doc = r" Default debug formatting for this struct (may be useful if you define your own `Debug` impl)"]
|
#[doc = r" 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 {
|
pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
zalsa_::with_attached_database(|db| {
|
zalsa_::with_attached_database(|db| {
|
||||||
let fields = Configuration_::ingredient(db).fields(db.as_dyn_database(), this);
|
let fields = Configuration_::ingredient(db).fields(db.zalsa(), this);
|
||||||
let mut f = f.debug_struct("InternedString");
|
let mut f = f.debug_struct("InternedString");
|
||||||
let f = f.field("data", &fields.0);
|
let f = f.field("data", &fields.0);
|
||||||
let f = f.field("other", &fields.1);
|
let f = f.field("other", &fields.1);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue