mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-01-23 05:07:27 +00:00
WIP
This commit is contained in:
parent
15d5f213c5
commit
2cfb75837b
10 changed files with 338 additions and 106 deletions
|
@ -11,6 +11,7 @@ description = "Procedural macros for the salsa crate"
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
heck = "0.5.0"
|
||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
syn = { version = "2.0.64", features = ["full", "visit-mut"] }
|
syn = { version = "2.0.64", features = ["full", "visit-mut"] }
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use proc_macro2::{Literal, TokenStream};
|
use proc_macro2::TokenStream;
|
||||||
use syn::{parse::Nothing, spanned::Spanned, Token};
|
use syn::{parse::Nothing, ItemStruct};
|
||||||
|
|
||||||
|
use crate::hygiene::Hygiene;
|
||||||
|
|
||||||
// Source:
|
// Source:
|
||||||
//
|
//
|
||||||
|
@ -12,37 +14,35 @@ pub(crate) fn db(
|
||||||
args: proc_macro::TokenStream,
|
args: proc_macro::TokenStream,
|
||||||
input: proc_macro::TokenStream,
|
input: proc_macro::TokenStream,
|
||||||
) -> proc_macro::TokenStream {
|
) -> proc_macro::TokenStream {
|
||||||
let args = syn::parse_macro_input!(args as Args);
|
let _nothing = syn::parse_macro_input!(args as Nothing);
|
||||||
let input = syn::parse_macro_input!(input as syn::ItemStruct);
|
let db_macro = DbMacro {
|
||||||
match args.try_db(&input) {
|
hygiene: Hygiene::from(&input),
|
||||||
Ok(v) => quote! { #input #v }.into(),
|
input: syn::parse_macro_input!(input as syn::ItemStruct),
|
||||||
Err(e) => {
|
};
|
||||||
let error = e.to_compile_error();
|
match db_macro.try_db() {
|
||||||
quote! { #input #error }.into()
|
Ok(v) => v.into(),
|
||||||
}
|
Err(e) => e.to_compile_error().into(),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Args {}
|
|
||||||
|
|
||||||
impl syn::parse::Parse for Args {
|
|
||||||
fn parse(_input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
|
|
||||||
Ok(Args {})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args {
|
struct DbMacro {
|
||||||
fn try_db(self, input: &syn::ItemStruct) -> syn::Result<TokenStream> {
|
hygiene: Hygiene,
|
||||||
let storage = self.find_storage_field(input)?;
|
input: ItemStruct,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DbMacro {
|
||||||
|
fn try_db(self) -> syn::Result<TokenStream> {
|
||||||
|
let has_storage_impl = self.has_storage_impl()?;
|
||||||
|
let input = self.input;
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
|
#has_storage_impl
|
||||||
#input
|
#input
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_storage_field(&self, input: &syn::ItemStruct) -> syn::Result<syn::Ident> {
|
fn find_storage_field(&self) -> syn::Result<syn::Ident> {
|
||||||
let storage = "storage";
|
let storage = "storage";
|
||||||
for field in input.fields.iter() {
|
for field in self.input.fields.iter() {
|
||||||
if let Some(i) = &field.ident {
|
if let Some(i) = &field.ident {
|
||||||
if i == storage {
|
if i == storage {
|
||||||
return Ok(i.clone());
|
return Ok(i.clone());
|
||||||
|
@ -56,8 +56,34 @@ impl Args {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Err(syn::Error::new_spanned(
|
return Err(syn::Error::new_spanned(
|
||||||
&input.ident,
|
&self.input.ident,
|
||||||
"database struct must be a braced struct (`{}`) with a field named `storage`",
|
"database struct must be a braced struct (`{}`) with a field named `storage`",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn has_storage_impl(&self) -> syn::Result<TokenStream> {
|
||||||
|
let storage = self.find_storage_field()?;
|
||||||
|
let db = &self.input.ident;
|
||||||
|
|
||||||
|
let SalsaHasStorage = self.hygiene.ident("SalsaHasStorage");
|
||||||
|
let SalsaStorage = self.hygiene.ident("SalsaStorage");
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
const _: () = {
|
||||||
|
use salsa::storage::HasStorage as #SalsaHasStorage;
|
||||||
|
use salsa::storage::Storage as #SalsaStorage;
|
||||||
|
|
||||||
|
unsafe impl #SalsaHasStorage for #db {
|
||||||
|
fn storage(&self) -> &#SalsaStorage<Self> {
|
||||||
|
&self.#storage
|
||||||
|
}
|
||||||
|
|
||||||
|
fn storage_mut(&mut self) -> &mut #SalsaStorage<Self> {
|
||||||
|
&mut self.#storage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,66 +1,153 @@
|
||||||
use proc_macro2::{Literal, TokenStream};
|
use std::fmt::Display;
|
||||||
use syn::{spanned::Spanned, Token};
|
|
||||||
|
use heck::ToSnakeCase;
|
||||||
|
use proc_macro2::{Span, TokenStream};
|
||||||
|
|
||||||
|
use crate::hygiene::Hygiene;
|
||||||
|
|
||||||
// Source:
|
// Source:
|
||||||
//
|
//
|
||||||
|
// ```
|
||||||
// #[salsa::db_view]
|
// #[salsa::db_view]
|
||||||
// pub trait Db: salsa::DatabaseView<dyn Db> + ... {
|
// pub trait $Db: ... {
|
||||||
// ...
|
// ...
|
||||||
// }
|
// }
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// becomes
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// pub trait $Db: __SalsaViewAs$Db__ {
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub trait __SalsaViewAs$Db__ {
|
||||||
|
// fn __salsa_add_view_for_$db__(&self);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// impl<T: Db> __SalsaViewAs$Db__ for T {
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
pub(crate) fn db_view(
|
pub(crate) fn db_view(
|
||||||
args: proc_macro::TokenStream,
|
args: proc_macro::TokenStream,
|
||||||
input: proc_macro::TokenStream,
|
input: proc_macro::TokenStream,
|
||||||
) -> proc_macro::TokenStream {
|
) -> proc_macro::TokenStream {
|
||||||
let args: TokenStream = args.into();
|
syn::parse_macro_input!(args as syn::parse::Nothing);
|
||||||
let input = syn::parse_macro_input!(input as syn::ItemTrait);
|
let db_view_macro = DbViewMacro::new(
|
||||||
match try_db_view(args, input) {
|
Hygiene::from(&input),
|
||||||
|
syn::parse_macro_input!(input as syn::ItemTrait),
|
||||||
|
);
|
||||||
|
|
||||||
|
match db_view_macro.expand() {
|
||||||
Ok(tokens) => tokens.into(),
|
Ok(tokens) => tokens.into(),
|
||||||
Err(err) => err.to_compile_error().into(),
|
Err(err) => err.to_compile_error().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_db_view(args: TokenStream, input: syn::ItemTrait) -> syn::Result<TokenStream> {
|
#[allow(non_snake_case)]
|
||||||
if let Some(token) = args.into_iter().next() {
|
pub(crate) struct DbViewMacro {
|
||||||
return Err(syn::Error::new_spanned(token, "unexpected token"));
|
hygiene: Hygiene,
|
||||||
|
input: syn::ItemTrait,
|
||||||
|
DbViewTrait: syn::Ident,
|
||||||
|
db_view_method: syn::Ident,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: check for `salsa::DataviewView<dyn Db>` supertrait?
|
#[allow(non_snake_case)]
|
||||||
|
impl DbViewMacro {
|
||||||
|
// This is a case where our hygiene mechanism is inadequate.
|
||||||
|
//
|
||||||
|
// We cannot know whether `DbViewTrait` is defined elsewhere
|
||||||
|
// in the module.
|
||||||
|
//
|
||||||
|
// Therefore we give it a dorky name.
|
||||||
|
|
||||||
let view_impl = view_impl(&input);
|
pub(crate) fn db_view_trait_name(input: &impl Display) -> syn::Ident {
|
||||||
|
syn::Ident::new(&format!("__SalsaAddView{}__", input), Span::call_site())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn db_view_method_name(input: &impl Display) -> syn::Ident {
|
||||||
|
syn::Ident::new(
|
||||||
|
&format!("__salsa_add_view_{}__", input.to_string().to_snake_case()),
|
||||||
|
Span::call_site(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(hygiene: Hygiene, input: syn::ItemTrait) -> Self {
|
||||||
|
Self {
|
||||||
|
DbViewTrait: Self::db_view_trait_name(&input.ident),
|
||||||
|
db_view_method: Self::db_view_method_name(&input.ident),
|
||||||
|
hygiene,
|
||||||
|
input,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand(mut self) -> syn::Result<TokenStream> {
|
||||||
|
self.add_supertrait();
|
||||||
|
let view_impl = self.view_impl();
|
||||||
|
let view_trait = self.view_trait();
|
||||||
|
|
||||||
|
let input = self.input;
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#input
|
#input
|
||||||
|
#view_trait
|
||||||
#view_impl
|
#view_impl
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
fn add_supertrait(&mut self) {
|
||||||
fn view_impl(input: &syn::ItemTrait) -> syn::Item {
|
let Self { DbViewTrait, .. } = self;
|
||||||
let DB = syn::Ident::new("_DB", proc_macro2::Span::call_site());
|
self.input.supertraits.push(parse_quote! { #DbViewTrait })
|
||||||
let Database = syn::Ident::new("_Database", proc_macro2::Span::call_site());
|
}
|
||||||
let DatabaseView = syn::Ident::new("_DatabaseView", proc_macro2::Span::call_site());
|
|
||||||
let upcasts = syn::Ident::new("_upcasts", proc_macro2::Span::call_site());
|
fn view_trait(&self) -> syn::ItemTrait {
|
||||||
let UserTrait = &input.ident;
|
let Self {
|
||||||
|
DbViewTrait,
|
||||||
|
db_view_method,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let vis = &self.input.vis;
|
||||||
|
parse_quote! {
|
||||||
|
/// Internal salsa method generated by the `salsa::db_view` macro
|
||||||
|
/// that registers this database view trait with the salsa database.
|
||||||
|
///
|
||||||
|
/// Nothing to see here.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#vis trait #DbViewTrait {
|
||||||
|
fn #db_view_method(&self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view_impl(&self) -> syn::Item {
|
||||||
|
let Self {
|
||||||
|
DbViewTrait,
|
||||||
|
db_view_method,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let DB = self.hygiene.ident("DB");
|
||||||
|
let Database = self.hygiene.ident("Database");
|
||||||
|
let views = self.hygiene.ident("views");
|
||||||
|
let UserTrait = &self.input.ident;
|
||||||
|
|
||||||
parse_quote! {
|
parse_quote! {
|
||||||
const _: () = {
|
const _: () = {
|
||||||
use salsa::DatabaseView as #DatabaseView;
|
|
||||||
use salsa::Database as #Database;
|
use salsa::Database as #Database;
|
||||||
|
|
||||||
impl<#DB: #Database> #DatabaseView<dyn #UserTrait> for #DB {
|
#[doc(hidden)]
|
||||||
fn add_view_to_db(&self) {
|
impl<#DB: #Database> #DbViewTrait for #DB {
|
||||||
let #upcasts = self.upcasts_for_self();
|
/// Internal salsa method generated by the `salsa::db_view` macro
|
||||||
#upcasts.add::<dyn #UserTrait>(|t| t, |t| t);
|
/// that registers this database view trait with the salsa database.
|
||||||
|
///
|
||||||
|
/// Nothing to see here.
|
||||||
|
fn #db_view_method(&self) {
|
||||||
|
let #views = self.views_of_self();
|
||||||
|
#views.add::<dyn #UserTrait>(|t| t, |t| t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Args {}
|
|
||||||
|
|
||||||
impl syn::parse::Parse for Args {
|
|
||||||
fn parse(_input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
|
|
||||||
Ok(Self {})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
41
components/salsa-macros/src/hygiene.rs
Normal file
41
components/salsa-macros/src/hygiene.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
pub struct Hygiene {
|
||||||
|
user_tokens: HashSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&proc_macro::TokenStream> for Hygiene {
|
||||||
|
fn from(input: &proc_macro::TokenStream) -> Self {
|
||||||
|
let mut user_tokens = HashSet::new();
|
||||||
|
push_idents(input.clone(), &mut user_tokens);
|
||||||
|
Self { user_tokens }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_idents(input: proc_macro::TokenStream, user_tokens: &mut HashSet<String>) {
|
||||||
|
input.into_iter().for_each(|token| match token {
|
||||||
|
proc_macro::TokenTree::Group(g) => {
|
||||||
|
push_idents(g.stream(), user_tokens);
|
||||||
|
}
|
||||||
|
proc_macro::TokenTree::Ident(ident) => {
|
||||||
|
user_tokens.insert(ident.to_string());
|
||||||
|
}
|
||||||
|
proc_macro::TokenTree::Punct(_) => (),
|
||||||
|
proc_macro::TokenTree::Literal(_) => (),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hygiene {
|
||||||
|
/// Generates an identifier similar to `text` but
|
||||||
|
/// distinct from any identifiers that appear in the user's
|
||||||
|
/// code.
|
||||||
|
pub(crate) fn ident(&self, text: &str) -> syn::Ident {
|
||||||
|
let mut buffer = String::from(text);
|
||||||
|
|
||||||
|
while self.user_tokens.contains(&buffer) {
|
||||||
|
buffer.push('_');
|
||||||
|
}
|
||||||
|
|
||||||
|
syn::Ident::new(&buffer, proc_macro2::Span::call_site())
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,8 @@ extern crate proc_macro2;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate quote;
|
extern crate quote;
|
||||||
|
|
||||||
|
mod hygiene;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
|
|
||||||
macro_rules! parse_quote {
|
macro_rules! parse_quote {
|
||||||
|
|
|
@ -47,7 +47,7 @@ pub trait DatabaseView<Dyn: ?Sized + Any>: Database {
|
||||||
|
|
||||||
impl<Db: Database> DatabaseView<dyn Database> for Db {
|
impl<Db: Database> DatabaseView<dyn Database> for Db {
|
||||||
fn add_view_to_db(&self) {
|
fn add_view_to_db(&self) {
|
||||||
let upcasts = self.upcasts_for_self();
|
let upcasts = self.views_of_self();
|
||||||
upcasts.add::<dyn Database>(|t| t, |t| t);
|
upcasts.add::<dyn Database>(|t| t, |t| t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ mod alloc;
|
||||||
pub mod cancelled;
|
pub mod cancelled;
|
||||||
pub mod cycle;
|
pub mod cycle;
|
||||||
pub mod database;
|
pub mod database;
|
||||||
mod downcast;
|
|
||||||
pub mod durability;
|
pub mod durability;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod function;
|
pub mod function;
|
||||||
|
@ -24,6 +23,7 @@ pub mod setter;
|
||||||
pub mod storage;
|
pub mod storage;
|
||||||
pub mod tracked_struct;
|
pub mod tracked_struct;
|
||||||
pub mod update;
|
pub mod update;
|
||||||
|
mod views;
|
||||||
|
|
||||||
pub use self::cancelled::Cancelled;
|
pub use self::cancelled::Cancelled;
|
||||||
pub use self::cycle::Cycle;
|
pub use self::cycle::Cycle;
|
||||||
|
@ -41,6 +41,7 @@ pub use self::runtime::Runtime;
|
||||||
pub use self::storage::Storage;
|
pub use self::storage::Storage;
|
||||||
pub use salsa_macros::accumulator;
|
pub use salsa_macros::accumulator;
|
||||||
pub use salsa_macros::db;
|
pub use salsa_macros::db;
|
||||||
|
pub use salsa_macros::db_view;
|
||||||
pub use salsa_macros::input;
|
pub use salsa_macros::input;
|
||||||
pub use salsa_macros::interned;
|
pub use salsa_macros::interned;
|
||||||
pub use salsa_macros::jar;
|
pub use salsa_macros::jar;
|
||||||
|
|
100
src/storage.rs
100
src/storage.rs
|
@ -6,41 +6,52 @@ use parking_lot::{Condvar, Mutex};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::cycle::CycleRecoveryStrategy;
|
use crate::cycle::CycleRecoveryStrategy;
|
||||||
use crate::downcast::{DynDowncasts, DynDowncastsFor};
|
|
||||||
use crate::ingredient::{Ingredient, Jar};
|
use crate::ingredient::{Ingredient, Jar};
|
||||||
use crate::nonce::{Nonce, NonceGenerator};
|
use crate::nonce::{Nonce, NonceGenerator};
|
||||||
use crate::runtime::Runtime;
|
use crate::runtime::Runtime;
|
||||||
|
use crate::views::{Views, ViewsOf};
|
||||||
use crate::Database;
|
use crate::Database;
|
||||||
|
|
||||||
use super::ParallelDatabase;
|
use super::ParallelDatabase;
|
||||||
|
|
||||||
/// Salsa database methods that are generated by the `#[salsa::database]` procedural macro.
|
/// Salsa database methods whose implementation is generated by
|
||||||
|
/// the `#[salsa::database]` procedural macro.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// This trait is meant to be implemented by our procedural macro.
|
/// This trait is meant to be implemented by our procedural macro.
|
||||||
/// We need to document any non-obvious conditions that it satisfies.
|
/// We need to document any non-obvious conditions that it satisfies.
|
||||||
pub unsafe trait DatabaseGen: Any + Send + Sync {
|
pub unsafe trait DatabaseGen: Any + Send + Sync {
|
||||||
|
/// Upcast to a `dyn Database`.
|
||||||
|
///
|
||||||
|
/// Only required because upcasts not yet stabilized (*grr*).
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Returns the same data pointer as `self`.
|
||||||
fn as_salsa_database(&self) -> &dyn Database;
|
fn as_salsa_database(&self) -> &dyn Database;
|
||||||
|
|
||||||
/// Returns a reference to the underlying "dyn-upcasts"
|
|
||||||
fn upcasts(&self) -> &DynDowncasts;
|
|
||||||
|
|
||||||
/// Upcast to a `dyn DatabaseGen`.
|
/// Upcast to a `dyn DatabaseGen`.
|
||||||
///
|
///
|
||||||
/// Only required because upcasts not yet stabilized (*grr*).
|
/// Only required because upcasts not yet stabilized (*grr*).
|
||||||
///
|
///
|
||||||
/// # Ensures
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Returns the same data pointer as `self`.
|
/// Returns the same data pointer as `self`.
|
||||||
fn upcast_to_dyn_database_gen(&self) -> &dyn DatabaseGen;
|
fn as_salsa_database_gen(&self) -> &dyn DatabaseGen;
|
||||||
|
|
||||||
|
/// Returns a reference to the underlying.
|
||||||
|
fn views(&self) -> &Views;
|
||||||
|
|
||||||
/// Returns the upcasts database, tied to the type of `Self`; cannot be used from `dyn DatabaseGen` objects.
|
/// Returns the upcasts database, tied to the type of `Self`; cannot be used from `dyn DatabaseGen` objects.
|
||||||
fn upcasts_for_self(&self) -> &DynDowncastsFor<Self>
|
fn views_of_self(&self) -> &ViewsOf<Self>
|
||||||
where
|
where
|
||||||
Self: Sized + Database;
|
Self: Sized + Database;
|
||||||
|
|
||||||
/// Returns the nonce for the underyling storage.
|
/// Returns the nonce for the underyling storage.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
/// This nonce is guaranteed to be unique for the database and never to be reused.
|
/// This nonce is guaranteed to be unique for the database and never to be reused.
|
||||||
fn nonce(&self) -> Nonce<StorageNonce>;
|
fn nonce(&self) -> Nonce<StorageNonce>;
|
||||||
|
|
||||||
|
@ -61,6 +72,65 @@ pub unsafe trait DatabaseGen: Any + Send + Sync {
|
||||||
fn runtime_mut(&mut self) -> &mut Runtime;
|
fn runtime_mut(&mut self) -> &mut Runtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This is the *actual* trait that the macro generates.
|
||||||
|
/// It simply gives access to the internal storage.
|
||||||
|
/// Note that it is NOT a supertrait of `Database`
|
||||||
|
/// because it is not `dyn`-safe.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The `storage` field must be an owned field of
|
||||||
|
/// the implementing struct.
|
||||||
|
pub unsafe trait HasStorage: Database + Sized + Any + Send + Sync {
|
||||||
|
fn storage(&self) -> &Storage<Self>;
|
||||||
|
fn storage_mut(&self) -> &mut Storage<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: HasStorage> DatabaseGen for T {
|
||||||
|
fn as_salsa_database(&self) -> &dyn Database {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_salsa_database_gen(&self) -> &dyn DatabaseGen {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn views(&self) -> &Views {
|
||||||
|
&self.storage().shared.upcasts
|
||||||
|
}
|
||||||
|
|
||||||
|
fn views_of_self(&self) -> &ViewsOf<Self>
|
||||||
|
where
|
||||||
|
Self: Sized + Database,
|
||||||
|
{
|
||||||
|
&self.storage().shared.upcasts
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nonce(&self) -> Nonce<StorageNonce> {
|
||||||
|
self.storage().shared.nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_jar_by_type(&self, jar: &dyn Jar) -> Option<IngredientIndex> {
|
||||||
|
self.storage().lookup_jar_by_type(jar)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_or_lookup_jar_by_type(&self, jar: &dyn Jar) -> IngredientIndex {
|
||||||
|
self.storage().add_or_lookup_jar_by_type(jar)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_ingredient(&self, index: IngredientIndex) -> &dyn Ingredient {
|
||||||
|
self.storage().lookup_ingredient(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runtime(&self) -> &Runtime {
|
||||||
|
&self.storage().runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runtime_mut(&mut self) -> &mut Runtime {
|
||||||
|
&mut self.storage_mut().runtime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl dyn Database {
|
impl dyn Database {
|
||||||
/// Upcasts `self` to the given view.
|
/// Upcasts `self` to the given view.
|
||||||
///
|
///
|
||||||
|
@ -68,7 +138,7 @@ impl dyn Database {
|
||||||
///
|
///
|
||||||
/// If the view has not been added to the database (see [`DatabaseView`][])
|
/// If the view has not been added to the database (see [`DatabaseView`][])
|
||||||
pub fn as_view<DbView: ?Sized + Database>(&self) -> &DbView {
|
pub fn as_view<DbView: ?Sized + Database>(&self) -> &DbView {
|
||||||
self.upcasts().try_cast(self).unwrap()
|
self.views().try_view_as(self).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Upcasts `self` to the given view.
|
/// Upcasts `self` to the given view.
|
||||||
|
@ -78,8 +148,8 @@ impl dyn Database {
|
||||||
/// If the view has not been added to the database (see [`DatabaseView`][])
|
/// If the view has not been added to the database (see [`DatabaseView`][])
|
||||||
pub fn as_view_mut<DbView: ?Sized + Database>(&mut self) -> &mut DbView {
|
pub fn as_view_mut<DbView: ?Sized + Database>(&mut self) -> &mut DbView {
|
||||||
// Avoid a borrow check error by cloning. This is the "uncommon" path so it seems fine.
|
// Avoid a borrow check error by cloning. This is the "uncommon" path so it seems fine.
|
||||||
let upcasts = self.upcasts().clone();
|
let upcasts = self.views().clone();
|
||||||
upcasts.try_cast_mut(self).unwrap()
|
upcasts.try_view_as_mut(self).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +208,7 @@ pub struct Storage<Db: Database> {
|
||||||
/// This is where the actual data for tracked functions, structs, inputs, etc lives,
|
/// This is where the actual data for tracked functions, structs, inputs, etc lives,
|
||||||
/// along with some coordination variables between treads.
|
/// along with some coordination variables between treads.
|
||||||
struct Shared<Db: Database> {
|
struct Shared<Db: Database> {
|
||||||
upcasts: DynDowncastsFor<Db>,
|
upcasts: ViewsOf<Db>,
|
||||||
|
|
||||||
nonce: Nonce<StorageNonce>,
|
nonce: Nonce<StorageNonce>,
|
||||||
|
|
||||||
|
@ -198,7 +268,7 @@ impl<Db: Database> Storage<Db> {
|
||||||
|
|
||||||
/// Adds the ingredients in `jar` to the database if not already present.
|
/// Adds the ingredients in `jar` to the database if not already present.
|
||||||
/// If a jar of this type is already present, returns the index.
|
/// If a jar of this type is already present, returns the index.
|
||||||
fn add_or_lookup_adapted_jar_by_type(&self, jar: &dyn Jar) -> IngredientIndex {
|
fn add_or_lookup_jar_by_type(&self, jar: &dyn Jar) -> IngredientIndex {
|
||||||
let jar_type_id = jar.type_id();
|
let jar_type_id = jar.type_id();
|
||||||
let mut jar_map = self.shared.jar_map.lock();
|
let mut jar_map = self.shared.jar_map.lock();
|
||||||
*jar_map
|
*jar_map
|
||||||
|
@ -225,8 +295,8 @@ impl<Db: Database> Storage<Db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the index of the 1st ingredient from the given jar.
|
/// Return the index of the 1st ingredient from the given jar.
|
||||||
pub fn lookup_jar_by_type(&self, jar_type_id: TypeId) -> Option<IngredientIndex> {
|
pub fn lookup_jar_by_type(&self, jar: &dyn Jar) -> Option<IngredientIndex> {
|
||||||
self.shared.jar_map.lock().get(&jar_type_id).copied()
|
self.shared.jar_map.lock().get(&jar.type_id()).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lookup_ingredient(&self, index: IngredientIndex) -> &dyn Ingredient {
|
pub fn lookup_ingredient(&self, index: IngredientIndex) -> &dyn Ingredient {
|
||||||
|
|
|
@ -9,18 +9,18 @@ use orx_concurrent_vec::ConcurrentVec;
|
||||||
|
|
||||||
use crate::Database;
|
use crate::Database;
|
||||||
|
|
||||||
pub struct DynDowncastsFor<Db: Database> {
|
pub struct ViewsOf<Db: Database> {
|
||||||
upcasts: DynDowncasts,
|
upcasts: Views,
|
||||||
phantom: PhantomData<Db>,
|
phantom: PhantomData<Db>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DynDowncasts {
|
pub struct Views {
|
||||||
source_type_id: TypeId,
|
source_type_id: TypeId,
|
||||||
vec: Arc<ConcurrentVec<Caster>>,
|
view_casters: Arc<ConcurrentVec<ViewCaster>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Caster {
|
struct ViewCaster {
|
||||||
target_type_id: TypeId,
|
target_type_id: TypeId,
|
||||||
type_name: &'static str,
|
type_name: &'static str,
|
||||||
func: fn(&Dummy) -> &Dummy,
|
func: fn(&Dummy) -> &Dummy,
|
||||||
|
@ -30,16 +30,16 @@ struct Caster {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
enum Dummy {}
|
enum Dummy {}
|
||||||
|
|
||||||
impl<Db: Database> Default for DynDowncastsFor<Db> {
|
impl<Db: Database> Default for ViewsOf<Db> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
upcasts: DynDowncasts::new::<Db>(),
|
upcasts: Views::new::<Db>(),
|
||||||
phantom: Default::default(),
|
phantom: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Db: Database> DynDowncastsFor<Db> {
|
impl<Db: Database> ViewsOf<Db> {
|
||||||
/// Add a new upcast from `Db` to `T`, given the upcasting function `func`.
|
/// Add a new upcast from `Db` to `T`, given the upcasting function `func`.
|
||||||
pub fn add<DbView: ?Sized + Any>(
|
pub fn add<DbView: ?Sized + Any>(
|
||||||
&self,
|
&self,
|
||||||
|
@ -50,20 +50,20 @@ impl<Db: Database> DynDowncastsFor<Db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Db: Database> Deref for DynDowncastsFor<Db> {
|
impl<Db: Database> Deref for ViewsOf<Db> {
|
||||||
type Target = DynDowncasts;
|
type Target = Views;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.upcasts
|
&self.upcasts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynDowncasts {
|
impl Views {
|
||||||
fn new<Db: Database>() -> Self {
|
fn new<Db: Database>() -> Self {
|
||||||
let source_type_id = TypeId::of::<Db>();
|
let source_type_id = TypeId::of::<Db>();
|
||||||
Self {
|
Self {
|
||||||
source_type_id,
|
source_type_id,
|
||||||
vec: Default::default(),
|
view_casters: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,11 +77,15 @@ impl DynDowncasts {
|
||||||
|
|
||||||
let target_type_id = TypeId::of::<DbView>();
|
let target_type_id = TypeId::of::<DbView>();
|
||||||
|
|
||||||
if self.vec.iter().any(|u| u.target_type_id == target_type_id) {
|
if self
|
||||||
|
.view_casters
|
||||||
|
.iter()
|
||||||
|
.any(|u| u.target_type_id == target_type_id)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.vec.push(Caster {
|
self.view_casters.push(ViewCaster {
|
||||||
target_type_id,
|
target_type_id,
|
||||||
type_name: std::any::type_name::<DbView>(),
|
type_name: std::any::type_name::<DbView>(),
|
||||||
func: unsafe { std::mem::transmute(func) },
|
func: unsafe { std::mem::transmute(func) },
|
||||||
|
@ -94,7 +98,7 @@ impl DynDowncasts {
|
||||||
/// # 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 upcasts was created for.
|
||||||
pub fn try_cast<'db, DbView: ?Sized + Any>(
|
pub fn try_view_as<'db, DbView: ?Sized + Any>(
|
||||||
&self,
|
&self,
|
||||||
db: &'db dyn Database,
|
db: &'db dyn Database,
|
||||||
) -> Option<&'db DbView> {
|
) -> Option<&'db DbView> {
|
||||||
|
@ -102,7 +106,7 @@ impl DynDowncasts {
|
||||||
assert_eq!(self.source_type_id, db_type_id, "database type mismatch");
|
assert_eq!(self.source_type_id, db_type_id, "database type mismatch");
|
||||||
|
|
||||||
let view_type_id = TypeId::of::<DbView>();
|
let view_type_id = TypeId::of::<DbView>();
|
||||||
for caster in self.vec.iter() {
|
for caster in self.view_casters.iter() {
|
||||||
if caster.target_type_id == view_type_id {
|
if caster.target_type_id == view_type_id {
|
||||||
// SAFETY: We have some function that takes a thin reference to the underlying
|
// SAFETY: We have some function that takes a thin reference to the underlying
|
||||||
// database type `X` and returns a (potentially wide) reference to `View`.
|
// database type `X` and returns a (potentially wide) reference to `View`.
|
||||||
|
@ -123,7 +127,7 @@ impl DynDowncasts {
|
||||||
/// # 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 upcasts was created for.
|
||||||
pub fn try_cast_mut<'db, View: ?Sized + Any>(
|
pub fn try_view_as_mut<'db, View: ?Sized + Any>(
|
||||||
&self,
|
&self,
|
||||||
db: &'db mut dyn Database,
|
db: &'db mut dyn Database,
|
||||||
) -> Option<&'db mut View> {
|
) -> Option<&'db mut View> {
|
||||||
|
@ -131,7 +135,7 @@ impl DynDowncasts {
|
||||||
assert_eq!(self.source_type_id, db_type_id, "database type mismatch");
|
assert_eq!(self.source_type_id, db_type_id, "database type mismatch");
|
||||||
|
|
||||||
let view_type_id = TypeId::of::<View>();
|
let view_type_id = TypeId::of::<View>();
|
||||||
for caster in self.vec.iter() {
|
for caster in self.view_casters.iter() {
|
||||||
if caster.target_type_id == view_type_id {
|
if caster.target_type_id == view_type_id {
|
||||||
// SAFETY: We have some function that takes a thin reference to the underlying
|
// SAFETY: We have some function that takes a thin reference to the underlying
|
||||||
// database type `X` and returns a (potentially wide) reference to `View`.
|
// database type `X` and returns a (potentially wide) reference to `View`.
|
||||||
|
@ -149,15 +153,15 @@ impl DynDowncasts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for DynDowncasts {
|
impl std::fmt::Debug for Views {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("DynDowncasts")
|
f.debug_struct("DynDowncasts")
|
||||||
.field("vec", &self.vec)
|
.field("vec", &self.view_casters)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Caster {
|
impl std::fmt::Debug for ViewCaster {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_tuple("DynDowncast").field(&self.type_name).finish()
|
f.debug_tuple("DynDowncast").field(&self.type_name).finish()
|
||||||
}
|
}
|
||||||
|
@ -179,7 +183,7 @@ fn data_ptr_mut<T: ?Sized>(t: &mut T) -> &mut () {
|
||||||
unsafe { &mut *u }
|
unsafe { &mut *u }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Db: Database> Clone for DynDowncastsFor<Db> {
|
impl<Db: Database> Clone for ViewsOf<Db> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
upcasts: self.upcasts.clone(),
|
upcasts: self.upcasts.clone(),
|
|
@ -8,7 +8,7 @@ use expect_test::expect;
|
||||||
use test_log::test;
|
use test_log::test;
|
||||||
|
|
||||||
#[salsa::db_view]
|
#[salsa::db_view]
|
||||||
trait Db: DatabaseView<dyn Db> + HasLogger {}
|
trait Db: HasLogger {}
|
||||||
|
|
||||||
#[salsa::input]
|
#[salsa::input]
|
||||||
struct MyInput {
|
struct MyInput {
|
||||||
|
|
Loading…
Reference in a new issue