port input to use Table

This commit is contained in:
Niko Matsakis 2024-08-14 08:07:28 +03:00
parent 2f8e78f431
commit 703f312eae
16 changed files with 247 additions and 207 deletions

View file

@ -203,7 +203,7 @@ macro_rules! setup_input_struct {
/// Default debug formatting for this struct (may be useful if you define your own `Debug` impl)
pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
$zalsa::with_attached_database(|db| {
let fields = $Configuration::ingredient(db).leak_fields(this);
let fields = $Configuration::ingredient(db).leak_fields(db, this);
let mut f = f.debug_struct(stringify!($Struct));
let f = f.field("[salsa id]", &$zalsa::AsId::as_id(&this));
$(
@ -235,7 +235,7 @@ macro_rules! setup_input_struct {
let current_revision = $zalsa::current_revision(db);
let ingredient = $Configuration::ingredient(db.as_dyn_database());
let (fields, stamps) = builder::builder_into_inner(self, current_revision);
ingredient.new_input(fields, stamps)
ingredient.new_input(db.as_dyn_database(), fields, stamps)
}
}

View file

@ -378,25 +378,25 @@ fn parse_print() {
let expected = expect_test::expect![[r#"
(
Program {
[salsa id]: Id(400),
[salsa id]: Id(800),
statements: [
Statement {
span: Span {
[salsa id]: Id(4),
[salsa id]: Id(404),
start: 0,
end: 11,
},
data: Print(
Expression {
span: Span {
[salsa id]: Id(3),
[salsa id]: Id(403),
start: 6,
end: 11,
},
data: Op(
Expression {
span: Span {
[salsa id]: Id(0),
[salsa id]: Id(400),
start: 6,
end: 7,
},
@ -407,7 +407,7 @@ fn parse_print() {
Add,
Expression {
span: Span {
[salsa id]: Id(2),
[salsa id]: Id(402),
start: 10,
end: 11,
},
@ -441,22 +441,22 @@ fn parse_example() {
let expected = expect_test::expect![[r#"
(
Program {
[salsa id]: Id(1000),
[salsa id]: Id(1400),
statements: [
Statement {
span: Span {
[salsa id]: Id(9),
[salsa id]: Id(409),
start: 13,
end: 57,
},
data: Function(
Function {
[salsa id]: Id(c00),
[salsa id]: Id(1000),
name: FunctionId {
text: "area_rectangle",
},
name_span: Span {
[salsa id]: Id(0),
[salsa id]: Id(400),
start: 16,
end: 30,
},
@ -470,14 +470,14 @@ fn parse_example() {
],
body: Expression {
span: Span {
[salsa id]: Id(8),
[salsa id]: Id(408),
start: 39,
end: 57,
},
data: Op(
Expression {
span: Span {
[salsa id]: Id(5),
[salsa id]: Id(405),
start: 39,
end: 41,
},
@ -490,7 +490,7 @@ fn parse_example() {
Multiply,
Expression {
span: Span {
[salsa id]: Id(7),
[salsa id]: Id(407),
start: 43,
end: 57,
},
@ -507,18 +507,18 @@ fn parse_example() {
},
Statement {
span: Span {
[salsa id]: Id(15),
[salsa id]: Id(415),
start: 57,
end: 102,
},
data: Function(
Function {
[salsa id]: Id(c01),
[salsa id]: Id(1001),
name: FunctionId {
text: "area_circle",
},
name_span: Span {
[salsa id]: Id(a),
[salsa id]: Id(40a),
start: 60,
end: 71,
},
@ -529,21 +529,21 @@ fn parse_example() {
],
body: Expression {
span: Span {
[salsa id]: Id(14),
[salsa id]: Id(414),
start: 77,
end: 102,
},
data: Op(
Expression {
span: Span {
[salsa id]: Id(11),
[salsa id]: Id(411),
start: 77,
end: 86,
},
data: Op(
Expression {
span: Span {
[salsa id]: Id(e),
[salsa id]: Id(40e),
start: 77,
end: 81,
},
@ -554,7 +554,7 @@ fn parse_example() {
Multiply,
Expression {
span: Span {
[salsa id]: Id(10),
[salsa id]: Id(410),
start: 84,
end: 86,
},
@ -569,7 +569,7 @@ fn parse_example() {
Multiply,
Expression {
span: Span {
[salsa id]: Id(13),
[salsa id]: Id(413),
start: 88,
end: 102,
},
@ -586,14 +586,14 @@ fn parse_example() {
},
Statement {
span: Span {
[salsa id]: Id(1c),
[salsa id]: Id(41c),
start: 102,
end: 141,
},
data: Print(
Expression {
span: Span {
[salsa id]: Id(1b),
[salsa id]: Id(41b),
start: 108,
end: 128,
},
@ -604,7 +604,7 @@ fn parse_example() {
[
Expression {
span: Span {
[salsa id]: Id(17),
[salsa id]: Id(417),
start: 123,
end: 124,
},
@ -614,7 +614,7 @@ fn parse_example() {
},
Expression {
span: Span {
[salsa id]: Id(19),
[salsa id]: Id(419),
start: 126,
end: 127,
},
@ -629,14 +629,14 @@ fn parse_example() {
},
Statement {
span: Span {
[salsa id]: Id(21),
[salsa id]: Id(421),
start: 141,
end: 174,
},
data: Print(
Expression {
span: Span {
[salsa id]: Id(20),
[salsa id]: Id(420),
start: 147,
end: 161,
},
@ -647,7 +647,7 @@ fn parse_example() {
[
Expression {
span: Span {
[salsa id]: Id(1e),
[salsa id]: Id(41e),
start: 159,
end: 160,
},
@ -662,21 +662,21 @@ fn parse_example() {
},
Statement {
span: Span {
[salsa id]: Id(26),
[salsa id]: Id(426),
start: 174,
end: 195,
},
data: Print(
Expression {
span: Span {
[salsa id]: Id(25),
[salsa id]: Id(425),
start: 180,
end: 186,
},
data: Op(
Expression {
span: Span {
[salsa id]: Id(22),
[salsa id]: Id(422),
start: 180,
end: 182,
},
@ -687,7 +687,7 @@ fn parse_example() {
Multiply,
Expression {
span: Span {
[salsa id]: Id(24),
[salsa id]: Id(424),
start: 185,
end: 186,
},
@ -714,7 +714,7 @@ fn parse_error() {
let expected = expect_test::expect![[r#"
(
Program {
[salsa id]: Id(400),
[salsa id]: Id(800),
statements: [],
},
[
@ -736,32 +736,32 @@ fn parse_precedence() {
let expected = expect_test::expect![[r#"
(
Program {
[salsa id]: Id(400),
[salsa id]: Id(800),
statements: [
Statement {
span: Span {
[salsa id]: Id(a),
[salsa id]: Id(40a),
start: 0,
end: 19,
},
data: Print(
Expression {
span: Span {
[salsa id]: Id(9),
[salsa id]: Id(409),
start: 6,
end: 19,
},
data: Op(
Expression {
span: Span {
[salsa id]: Id(6),
[salsa id]: Id(406),
start: 6,
end: 16,
},
data: Op(
Expression {
span: Span {
[salsa id]: Id(0),
[salsa id]: Id(400),
start: 6,
end: 7,
},
@ -772,14 +772,14 @@ fn parse_precedence() {
Add,
Expression {
span: Span {
[salsa id]: Id(5),
[salsa id]: Id(405),
start: 10,
end: 15,
},
data: Op(
Expression {
span: Span {
[salsa id]: Id(2),
[salsa id]: Id(402),
start: 10,
end: 11,
},
@ -790,7 +790,7 @@ fn parse_precedence() {
Multiply,
Expression {
span: Span {
[salsa id]: Id(4),
[salsa id]: Id(404),
start: 14,
end: 15,
},
@ -805,7 +805,7 @@ fn parse_precedence() {
Add,
Expression {
span: Span {
[salsa id]: Id(8),
[salsa id]: Id(408),
start: 18,
end: 19,
},

View file

@ -268,7 +268,7 @@ fn fix_bad_variable_in_function() {
expect![[r#"
[
"Event: Event { thread_id: ThreadId(11), kind: WillExecute { database_key: parse_statements(Id(0)) } }",
"Event: Event { thread_id: ThreadId(11), kind: WillExecute { database_key: type_check_function(Id(1400)) } }",
"Event: Event { thread_id: ThreadId(11), kind: WillExecute { database_key: type_check_function(Id(1800)) } }",
]
"#]],
)],

View file

@ -1,16 +1,11 @@
use std::{
any::Any,
fmt,
ops::DerefMut,
sync::atomic::{AtomicU32, Ordering},
};
use std::{any::Any, fmt, ops::DerefMut};
pub mod input_field;
pub mod setter;
mod struct_map;
use crossbeam::atomic::AtomicCell;
use input_field::FieldIngredientImpl;
use struct_map::StructMap;
use parking_lot::Mutex;
use crate::{
cycle::CycleRecoveryStrategy,
@ -18,7 +13,8 @@ use crate::{
ingredient::{fmt_index, Ingredient},
key::{DatabaseKeyIndex, DependencyIndex},
plumbing::{Jar, JarAux, Stamp},
zalsa::IngredientIndex,
table::{memo::MemoTable, Slot, Table},
zalsa::{IngredientIndex, Zalsa},
zalsa_local::QueryOrigin,
Database, Durability, Id, Revision, Runtime,
};
@ -57,15 +53,10 @@ impl<C: Configuration> Jar for JarImpl<C> {
struct_index: crate::zalsa::IngredientIndex,
) -> Vec<Box<dyn Ingredient>> {
let struct_ingredient: IngredientImpl<C> = IngredientImpl::new(struct_index);
let struct_map = struct_ingredient.struct_map.clone();
std::iter::once(Box::new(struct_ingredient) as _)
.chain((0..C::FIELD_DEBUG_NAMES.len()).map(|field_index| {
Box::new(FieldIngredientImpl::new(
struct_index,
field_index,
struct_map.clone(),
)) as _
Box::new(<FieldIngredientImpl<C>>::new(struct_index, field_index)) as _
}))
.collect()
}
@ -73,8 +64,8 @@ impl<C: Configuration> Jar for JarImpl<C> {
pub struct IngredientImpl<C: Configuration> {
ingredient_index: IngredientIndex,
counter: AtomicU32,
struct_map: StructMap<C>,
singleton_index: AtomicCell<Option<Id>>,
singleton_lock: Mutex<()>,
_phantom: std::marker::PhantomData<C::Struct>,
}
@ -82,12 +73,20 @@ impl<C: Configuration> IngredientImpl<C> {
pub fn new(index: IngredientIndex) -> Self {
Self {
ingredient_index: index,
counter: Default::default(),
struct_map: StructMap::new(),
singleton_index: AtomicCell::new(None),
singleton_lock: Default::default(),
_phantom: std::marker::PhantomData,
}
}
fn data<'db>(zalsa: &'db Zalsa, id: Id) -> &'db Value<C> {
zalsa.table().get(id)
}
fn data_raw<'db>(table: &'db Table, id: Id) -> *mut Value<C> {
table.get_raw(id)
}
pub fn database_key_index(&self, id: C::Struct) -> DatabaseKeyIndex {
DatabaseKeyIndex {
ingredient_index: self.ingredient_index,
@ -95,19 +94,36 @@ impl<C: Configuration> IngredientImpl<C> {
}
}
pub fn new_input(&self, fields: C::Fields, stamps: C::Stamps) -> C::Struct {
pub fn new_input(&self, db: &dyn Database, fields: C::Fields, stamps: C::Stamps) -> C::Struct {
let (zalsa, zalsa_local) = db.zalsas();
// If declared as a singleton, only allow a single instance
if C::IS_SINGLETON && self.counter.load(Ordering::Relaxed) >= 1 {
panic!("singleton struct may not be duplicated");
let guard = if C::IS_SINGLETON {
let guard = self.singleton_lock.lock();
if self.singleton_index.load().is_some() {
panic!("singleton struct may not be duplicated");
}
Some(guard)
} else {
None
};
let id = zalsa_local.allocate(
zalsa.table(),
self.ingredient_index,
Value::<C> {
fields,
stamps,
memos: Default::default(),
},
);
if C::IS_SINGLETON {
self.singleton_index.store(Some(id));
drop(guard);
}
let next_id = Id::from_u32(self.counter.fetch_add(1, Ordering::Relaxed));
let value = Value {
id: next_id,
fields,
stamps,
};
self.struct_map.insert(value)
FromId::from_id(id)
}
/// Change the value of the field `field_index` to a new value.
@ -128,7 +144,12 @@ impl<C: Configuration> IngredientImpl<C> {
setter: impl FnOnce(&mut C::Fields) -> R,
) -> R {
let id: Id = id.as_id();
let mut r = self.struct_map.update(id);
let r = Self::data_raw(runtime.table(), id);
// SAFETY: We hold `&mut` on the runtime so no `&`-references can be active.
// Also, we don't access any other data from the table while `r` is active.
let r = unsafe { &mut *r };
let stamp = &mut r.stamps[field_index];
if stamp.durability != Durability::LOW {
@ -146,7 +167,7 @@ impl<C: Configuration> IngredientImpl<C> {
C::IS_SINGLETON,
"get_singleton_input invoked on a non-singleton"
);
(self.counter.load(Ordering::Relaxed) > 0).then(|| C::Struct::from_id(Id::from_u32(0)))
self.singleton_index.load().map(FromId::from_id)
}
/// Access field of an input.
@ -158,10 +179,10 @@ impl<C: Configuration> IngredientImpl<C> {
id: C::Struct,
field_index: usize,
) -> &'db C::Fields {
let zalsa_local = db.zalsa_local();
let (zalsa, zalsa_local) = db.zalsas();
let field_ingredient_index = self.ingredient_index.successor(field_index);
let id = id.as_id();
let value = self.struct_map.get(id);
let value = Self::data(zalsa, id);
let stamp = &value.stamps[field_index];
zalsa_local.report_tracked_read(
DependencyIndex {
@ -176,9 +197,10 @@ impl<C: Configuration> IngredientImpl<C> {
/// Peek at the field values without recording any read dependency.
/// Used for debug printouts.
pub fn leak_fields(&self, id: C::Struct) -> &C::Fields {
pub fn leak_fields<'db>(&'db self, db: &'db dyn Database, id: C::Struct) -> &'db C::Fields {
let zalsa = db.zalsa();
let id = id.as_id();
let value = self.struct_map.get(id);
let value = Self::data(zalsa, id);
&value.fields
}
}
@ -267,17 +289,26 @@ pub struct Value<C>
where
C: Configuration,
{
/// The id of this struct in the ingredient.
id: Id,
/// Fields of this input struct. They can change across revisions,
/// but they do not change within a particular revision.
fields: C::Fields,
/// The revision and durability information for each field: when did this field last change.
stamps: C::Stamps,
/// Memos
memos: MemoTable,
}
pub trait HasBuilder {
type Builder;
}
impl<C> Slot for Value<C>
where
C: Configuration,
{
fn memos(&self) -> Option<&crate::table::memo::MemoTable> {
Some(&self.memos)
}
}

View file

@ -5,8 +5,9 @@ use crate::zalsa::IngredientIndex;
use crate::zalsa_local::QueryOrigin;
use crate::{Database, DatabaseKeyIndex, Id, Revision};
use std::fmt;
use std::marker::PhantomData;
use super::struct_map::StructMap;
use super::{IngredientImpl, Value};
/// Ingredient used to represent the fields of a `#[salsa::input]`.
///
@ -20,22 +21,18 @@ use super::struct_map::StructMap;
pub struct FieldIngredientImpl<C: Configuration> {
index: IngredientIndex,
field_index: usize,
struct_map: StructMap<C>,
phantom: PhantomData<fn() -> Value<C>>,
}
impl<C> FieldIngredientImpl<C>
where
C: Configuration,
{
pub(super) fn new(
struct_index: IngredientIndex,
field_index: usize,
struct_map: StructMap<C>,
) -> Self {
pub(super) fn new(struct_index: IngredientIndex, field_index: usize) -> Self {
Self {
index: struct_index.successor(field_index),
field_index,
struct_map,
phantom: PhantomData,
}
}
}
@ -54,12 +51,14 @@ where
fn maybe_changed_after(
&self,
_db: &dyn Database,
db: &dyn Database,
input: Option<Id>,
revision: Revision,
) -> bool {
let zalsa = db.zalsa();
let input = input.unwrap();
self.struct_map.get(input).stamps[self.field_index].changed_at > revision
let value = <IngredientImpl<C>>::data(zalsa, input);
value.stamps[self.field_index].changed_at > revision
}
fn origin(&self, _db: &dyn Database, _key_index: Id) -> Option<QueryOrigin> {

View file

@ -1,88 +0,0 @@
use std::{ops::DerefMut, sync::Arc};
use dashmap::mapref::one::RefMut;
use crate::{alloc::Alloc, hash::FxDashMap, id::FromId, Id};
use super::{Configuration, Value};
/// Maps an input id to its data.
///
/// Input structs can only be mutated by a call to a setter with an `&mut`
/// reference to the database, and therefore cannot be mutated during a tracked
/// function or in parallel.
///
/// However for on-demand inputs to work the fields must be able to be set via
/// a shared reference, so some locking is required.
///
/// Altogether this makes the implementation somewhat simpler than tracked
/// structs.
pub(crate) struct StructMap<C>
where
C: Configuration,
{
map: Arc<FxDashMap<Id, Alloc<Value<C>>>>,
}
impl<C: Configuration> Clone for StructMap<C> {
fn clone(&self) -> Self {
Self {
map: self.map.clone(),
}
}
}
impl<C> StructMap<C>
where
C: Configuration,
{
pub fn new() -> Self {
Self {
map: Arc::new(FxDashMap::default()),
}
}
/// Insert the given tracked struct value into the map.
///
/// # Panics
///
/// * If value with same `value.id` is already present in the map.
/// * If value not created in current revision.
pub fn insert(&self, value: Value<C>) -> C::Struct {
let id = value.id;
let boxed_value = Alloc::new(value);
let old_value = self.map.insert(id, boxed_value);
assert!(old_value.is_none()); // ...strictly speaking we probably need to abort here
C::Struct::from_id(id)
}
/// Get immutable access to the data for `id` -- this holds a write lock for the duration
/// of the returned value.
///
/// # Panics
///
/// * If the value is not present in the map.
/// * If the value is already updated in this revision.
pub fn get(&self, id: Id) -> &Value<C> {
/// More limited wrapper around transmute that copies lifetime from `a` to `b`.
///
/// # Safety condition
///
/// `b` must be owned by `a`
unsafe fn transmute_lifetime<'a, A, B>(_a: &'a A, b: &B) -> &'a B {
std::mem::transmute(b)
}
unsafe { transmute_lifetime(self, self.map.get(&id).unwrap().as_ref()) }
}
/// Get mutable access to the data for `id` -- this holds a write lock for the duration
/// of the returned value.
///
/// # Panics
///
/// * If the value is not present in the map.
/// * If the value is already updated in this revision.
pub fn update(&mut self, id: Id) -> impl DerefMut<Target = Value<C>> + '_ {
RefMut::map(self.map.get_mut(&id).unwrap(), |v| unsafe { v.as_mut() })
}
}

View file

@ -9,8 +9,8 @@ use parking_lot::Mutex;
use crate::{
active_query::ActiveQuery, cycle::CycleRecoveryStrategy, durability::Durability,
key::DatabaseKeyIndex, revision::AtomicRevision, zalsa_local::ZalsaLocal, Cancelled, Cycle,
Database, Event, EventKind, Revision,
key::DatabaseKeyIndex, revision::AtomicRevision, table::Table, zalsa_local::ZalsaLocal,
Cancelled, Cycle, Database, Event, EventKind, Revision,
};
use self::dependency_graph::DependencyGraph;
@ -40,6 +40,9 @@ pub struct Runtime {
/// The dependency graph tracks which runtimes are blocked on one
/// another, waiting for queries to terminate.
dependency_graph: Mutex<DependencyGraph>,
/// Data for instances
table: Table,
}
#[derive(Clone, Debug)]
@ -84,6 +87,7 @@ impl Default for Runtime {
next_id: AtomicUsize::new(1),
revision_canceled: Default::default(),
dependency_graph: Default::default(),
table: Default::default(),
}
}
}
@ -134,6 +138,14 @@ impl Runtime {
self.revision_canceled.store(true);
}
pub(crate) fn table(&self) -> &Table {
&self.table
}
pub(crate) fn table_mut(&mut self) -> &mut Table {
&mut self.table
}
/// Increments the "current revision" counter and clears
/// the cancellation flag.
///

View file

@ -9,7 +9,10 @@ use crossbeam::atomic::AtomicCell;
use memo::MemoTable;
use parking_lot::Mutex;
use crate::{zalsa::transmute_data_ptr, Id, IngredientIndex};
use crate::{
zalsa::{transmute_data_ptr, transmute_data_ptr_mut},
Id, IngredientIndex,
};
pub(crate) mod memo;
@ -75,22 +78,62 @@ impl Default for Table {
}
impl Table {
/// Get a reference to the data for `id`, which must have been allocated from this table with type `T`.
///
/// # Panics
///
/// If `id` is out of bounds or the does not have the type `T`.
pub fn get<T: Slot>(&self, id: Id) -> &T {
let (page, slot) = split_id(id);
let page_ref = self.page::<T>(page);
page_ref.get(slot)
}
/// Get a mutable reference to the data for `id`, which must have been allocated from this table with type `T`.
///
/// # Panics
///
/// If `id` is out of bounds or the does not have the type `T`.
pub fn get_mut<T: Slot>(&mut self, id: Id) -> &mut T {
let (page, slot) = split_id(id);
let page_ref = self.page_mut::<T>(page);
page_ref.get_mut(slot)
}
/// Get a raw pointer to the data for `id`, which must have been allocated from this table.
///
/// # Panics
///
/// If `id` is out of bounds or the does not have the type `T`.
///
/// # Safety
///
/// See [`Page::get_raw`][].
pub fn get_raw<T: Slot>(&self, id: Id) -> *mut T {
let (page, slot) = split_id(id);
let page_ref = self.page::<T>(page);
page_ref.get_raw(slot)
}
/// Gets a reference to the page which has slots of type `T`
///
/// # Panics
///
/// If `page` is out of bounds or the type `T` is incorrect.
pub fn page<T: Slot>(&self, page: PageIndex) -> &Page<T> {
self.pages[page.0].assert_type::<Page<T>>()
}
/// Gets a mutable reference to the page which has slots of type `T`
///
/// # Panics
///
/// If `page` is out of bounds or the type `T` is incorrect.
fn page_mut<T: Slot>(&mut self, page: PageIndex) -> &mut Page<T> {
self.pages[page.0].assert_type_mut::<Page<T>>()
}
/// Allocate a new page for the given ingredient and with slots of type `T`
pub fn push_page<T: Slot>(&self, ingredient: IngredientIndex) -> PageIndex {
let page = Box::new(<Page<T>>::new(ingredient));
PageIndex(self.pages.push(page))
@ -125,13 +168,36 @@ impl<T: Slot> Page<T> {
);
}
/// Returns a reference to the given slot.
///
/// # Panics
///
/// If slot is out of bounds
pub(crate) fn get(&self, slot: SlotIndex) -> &T {
self.check_bounds(slot);
unsafe { &*self.data[slot.0].get() }
}
/// Returns a mut reference to the given slot.
///
/// # Panics
///
/// If slot is out of bounds
pub(crate) fn get_mut(&mut self, slot: SlotIndex) -> &mut T {
self.check_bounds(slot);
self.data[slot.0].get_mut()
}
/// Returns a raw pointer to the given slot.
/// Reads/writes must be coordinated properly with calls to `get`.
///
/// # Panics
///
/// If slot is out of bounds
///
/// # Safety
///
/// Safe to call, but reads/writes through this pointer must be coordinated
/// properly with calls to [`get`](`Self::get`) and [`get_mut`](`Self::get_mut`).
pub(crate) fn get_raw(&self, slot: SlotIndex) -> *mut T {
self.check_bounds(slot);
self.data[slot.0].get()
@ -183,7 +249,7 @@ impl<T: Slot> Drop for Page<T> {
impl dyn TablePage {
fn assert_type<T: Any>(&self) -> &T {
assert_eq!(
self.type_id(),
Any::type_id(self),
TypeId::of::<T>(),
"page has hidden type `{:?}` but `{:?}` was expected",
self.hidden_type_name(),
@ -193,6 +259,19 @@ impl dyn TablePage {
// SAFETY: Assertion above
unsafe { transmute_data_ptr::<dyn TablePage, T>(self) }
}
fn assert_type_mut<T: Any>(&mut self) -> &mut T {
assert_eq!(
Any::type_id(self),
TypeId::of::<T>(),
"page has hidden type `{:?}` but `{:?}` was expected",
self.hidden_type_name(),
std::any::type_name::<T>(),
);
// SAFETY: Assertion above
unsafe { transmute_data_ptr_mut::<dyn TablePage, T>(self) }
}
}
fn make_id(page: PageIndex, slot: SlotIndex) -> Id {

View file

@ -132,9 +132,6 @@ pub struct Zalsa {
/// The runtime for this particular salsa database handle.
/// Each handle gets its own runtime, but the runtimes have shared state between them.
runtime: Runtime,
/// Data for instances
table: Table,
}
impl Zalsa {
@ -146,7 +143,6 @@ impl Zalsa {
ingredients_vec: AppendOnlyVec::new(),
ingredients_requiring_reset: AppendOnlyVec::new(),
runtime: Runtime::default(),
table: Table::default(),
memo_ingredient_count: AtomicCell::new(0),
}
}
@ -160,7 +156,7 @@ impl Zalsa {
}
pub(crate) fn table(&self) -> &Table {
&self.table
self.runtime.table()
}
/// **NOT SEMVER STABLE**
@ -349,3 +345,14 @@ pub(crate) unsafe fn transmute_data_ptr<T: ?Sized, U>(t: &T) -> &U {
let u: *const U = t as *const U;
unsafe { &*u }
}
/// Given a wide pointer `T`, extracts the data pointer (typed as `U`).
///
/// # Safety requirement
///
/// `U` must be correct type for the data pointer.
pub(crate) unsafe fn transmute_data_ptr_mut<T: ?Sized, U>(t: &mut T) -> &mut U {
let t: *mut T = t;
let u: *mut U = t as *mut U;
unsafe { &mut *u }
}

View file

@ -30,7 +30,7 @@ fn input() {
// debug includes all fields
let actual = format!("{complex_struct:?}");
let expected = expect![[r#"ComplexStruct { [salsa id]: Id(0), my_input: MyInput { [salsa id]: Id(0), field: 22 }, not_salsa: NotSalsa { field: "it's salsa time" } }"#]];
let expected = expect![[r#"ComplexStruct { [salsa id]: Id(400), my_input: MyInput { [salsa id]: Id(0), field: 22 }, not_salsa: NotSalsa { field: "it's salsa time" } }"#]];
expected.assert_eq(&actual);
})
}

View file

@ -80,11 +80,11 @@ fn basic() {
db.assert_logs(expect![[r#"
[
"intermediate_result(MyInput { [salsa id]: Id(0), field: 2 })",
"salsa_event(WillDiscardStaleOutput { execute_key: create_tracked_structs(Id(0)), output_key: MyTracked(Id(2)) })",
"salsa_event(DidDiscard { key: MyTracked(Id(2)) })",
"salsa_event(DidDiscard { key: contribution_from_struct(Id(2)) })",
"salsa_event(DidDiscard { key: MyTracked(Id(5)) })",
"salsa_event(DidDiscard { key: copy_field(Id(5)) })",
"salsa_event(WillDiscardStaleOutput { execute_key: create_tracked_structs(Id(0)), output_key: MyTracked(Id(402)) })",
"salsa_event(DidDiscard { key: MyTracked(Id(402)) })",
"salsa_event(DidDiscard { key: contribution_from_struct(Id(402)) })",
"salsa_event(DidDiscard { key: MyTracked(Id(405)) })",
"salsa_event(DidDiscard { key: copy_field(Id(405)) })",
"final_result(MyInput { [salsa id]: Id(0), field: 2 })",
]"#]]);
}

View file

@ -67,9 +67,9 @@ fn basic() {
db.assert_logs(expect![[r#"
[
"intermediate_result(MyInput { [salsa id]: Id(0), field: 2 })",
"salsa_event(WillDiscardStaleOutput { execute_key: create_tracked_structs(Id(0)), output_key: MyTracked(Id(2)) })",
"salsa_event(DidDiscard { key: MyTracked(Id(2)) })",
"salsa_event(DidDiscard { key: contribution_from_struct(Id(2)) })",
"salsa_event(WillDiscardStaleOutput { execute_key: create_tracked_structs(Id(0)), output_key: MyTracked(Id(402)) })",
"salsa_event(DidDiscard { key: MyTracked(Id(402)) })",
"salsa_event(DidDiscard { key: contribution_from_struct(Id(402)) })",
"final_result(MyInput { [salsa id]: Id(0), field: 2 })",
]"#]]);
}

View file

@ -64,7 +64,7 @@ fn test_leaked_inputs_ignored() {
"Event { thread_id: ThreadId(2), kind: WillCheckCancellation }",
"Event { thread_id: ThreadId(2), kind: WillExecute { database_key: function(Id(0)) } }",
"Event { thread_id: ThreadId(2), kind: WillCheckCancellation }",
"Event { thread_id: ThreadId(2), kind: WillExecute { database_key: counter_field(Id(400)) } }",
"Event { thread_id: ThreadId(2), kind: WillExecute { database_key: counter_field(Id(800)) } }",
]"#]]);
assert_eq!(result_in_rev_1, (0, 0));
@ -82,7 +82,7 @@ fn test_leaked_inputs_ignored() {
"Event { thread_id: ThreadId(2), kind: DidSetCancellationFlag }",
"Event { thread_id: ThreadId(2), kind: WillCheckCancellation }",
"Event { thread_id: ThreadId(2), kind: WillCheckCancellation }",
"Event { thread_id: ThreadId(2), kind: WillExecute { database_key: counter_field(Id(400)) } }",
"Event { thread_id: ThreadId(2), kind: WillExecute { database_key: counter_field(Id(800)) } }",
"Event { thread_id: ThreadId(2), kind: WillExecute { database_key: function(Id(0)) } }",
"Event { thread_id: ThreadId(2), kind: WillCheckCancellation }",
]"#]]);

View file

@ -61,7 +61,7 @@ fn test_leaked_inputs_ignored() {
"Event { thread_id: ThreadId(2), kind: WillCheckCancellation }",
"Event { thread_id: ThreadId(2), kind: WillExecute { database_key: function(Id(0)) } }",
"Event { thread_id: ThreadId(2), kind: WillCheckCancellation }",
"Event { thread_id: ThreadId(2), kind: WillExecute { database_key: counter_field(Id(0)) } }",
"Event { thread_id: ThreadId(2), kind: WillExecute { database_key: counter_field(Id(400)) } }",
]"#]]);
assert_eq!(result_in_rev_1, (0, 0));
@ -79,7 +79,7 @@ fn test_leaked_inputs_ignored() {
"Event { thread_id: ThreadId(2), kind: DidSetCancellationFlag }",
"Event { thread_id: ThreadId(2), kind: WillCheckCancellation }",
"Event { thread_id: ThreadId(2), kind: WillCheckCancellation }",
"Event { thread_id: ThreadId(2), kind: DidValidateMemoizedValue { database_key: counter_field(Id(0)) } }",
"Event { thread_id: ThreadId(2), kind: DidValidateMemoizedValue { database_key: counter_field(Id(400)) } }",
"Event { thread_id: ThreadId(2), kind: WillExecute { database_key: function(Id(0)) } }",
"Event { thread_id: ThreadId(2), kind: WillCheckCancellation }",
]"#]]);

View file

@ -64,7 +64,7 @@ fn execute() {
[
"salsa_event(WillExecute { database_key: the_fn(Id(0)) })",
"salsa_event(WillExecute { database_key: make_tracked_struct(Id(0)) })",
"salsa_event(WillExecute { database_key: read_tracked_struct(Id(0)) })",
"salsa_event(WillExecute { database_key: read_tracked_struct(Id(400)) })",
]"#]]);
// Update the input to `false` and re-execute.
@ -79,7 +79,7 @@ fn execute() {
db.assert_logs(expect![[r#"
[
"salsa_event(WillExecute { database_key: make_tracked_struct(Id(0)) })",
"salsa_event(DidValidateMemoizedValue { database_key: read_tracked_struct(Id(0)) })",
"salsa_event(DidValidateMemoizedValue { database_key: read_tracked_struct(Id(400)) })",
"salsa_event(DidValidateMemoizedValue { database_key: the_fn(Id(0)) })",
]"#]]);
}

View file

@ -36,14 +36,14 @@ fn execute() {
let t1 = create_tracked_list(db, input);
expect_test::expect![[r#"
MyTracked {
[salsa id]: Id(1),
[salsa id]: Id(401),
data: MyInput {
[salsa id]: Id(0),
field: "foo",
},
next: Next(
MyTracked {
[salsa id]: Id(0),
[salsa id]: Id(400),
data: MyInput {
[salsa id]: Id(0),
field: "foo",