ensure tracked struct ingredients are mapped correctly to tracked fields

This commit is contained in:
Ibraheem Ahmed 2025-01-17 20:07:27 -05:00
parent c90c2f2e8a
commit 9436f8ff6b
5 changed files with 94 additions and 23 deletions

View file

@ -2,49 +2,49 @@
#[macro_export]
macro_rules! setup_tracked_struct {
(
// Attributes on the function
// Attributes on the function.
attrs: [$(#[$attr:meta]),*],
// Visibility of the struct
// Visibility of the struct.
vis: $vis:vis,
// Name of the struct
// Name of the struct.
Struct: $Struct:ident,
// Name of the `'db` lifetime that the user gave
// Name of the `'db` lifetime that the user gave.
db_lt: $db_lt:lifetime,
// Name user gave for `new`
// Name user gave for `new`.
new_fn: $new_fn:ident,
// Field names
// Field names.
field_ids: [$($field_id:ident),*],
// Tracked field names
// Tracked field names.
tracked_ids: [$($tracked_id:ident),*],
// Tracked field names
// Visibility and names of tracked fields.
tracked_getters: [$($tracked_getter_vis:vis $tracked_getter_id:ident),*],
// Untracked field names
// Visibility and names of untracked fields.
untracked_getters: [$($untracked_getter_vis:vis $untracked_getter_id:ident),*],
// Field types, may reference `db_lt`
// Field types, may reference `db_lt`.
field_tys: [$($field_ty:ty),*],
// Tracked field types
// Tracked field types.
tracked_tys: [$($tracked_ty:ty),*],
// Untracked field types
// Untracked field types.
untracked_tys: [$($untracked_ty:ty),*],
// Indices for each field from 0..N -- must be unsuffixed (e.g., `0`, `1`).
field_indices: [$($field_index:tt),*],
// Indices of tracked fields.
// Absolute indices of any tracked fields, relative to all other fields of this struct.
tracked_indices: [$($tracked_index:tt),*],
// Indices of untracked fields.
// Absolute indices of any untracked fields.
untracked_indices: [$($untracked_index:tt),*],
// A set of "field options" for each field.
@ -64,7 +64,7 @@ macro_rules! setup_tracked_struct {
// A set of "field options" for each untracked field.
untracked_options: [$($untracked_option:tt),*],
// Number of fields
// Number of fields.
num_fields: $N:literal,
// If true, generate a debug impl.
@ -107,10 +107,6 @@ macro_rules! setup_tracked_struct {
$(stringify!($field_id),)*
];
const TRACKED_FIELD_DEBUG_NAMES: &'static [&'static str] = &[
$(stringify!($tracked_id),)*
];
type Fields<$db_lt> = ($($field_ty,)*);
type Revisions = $zalsa::Array<$Revision, $N>;

View file

@ -84,22 +84,29 @@ impl Macro {
let struct_ident = &self.struct_item.ident;
let db_lt = db_lifetime::db_lifetime(&self.struct_item.generics);
let new_fn = salsa_struct.constructor_name();
let field_ids = salsa_struct.field_ids();
let tracked_ids = salsa_struct.tracked_ids();
let tracked_vis = salsa_struct.tracked_vis();
let untracked_vis = salsa_struct.untracked_vis();
let tracked_getter_ids = salsa_struct.tracked_getter_ids();
let untracked_getter_ids = salsa_struct.untracked_getter_ids();
let field_indices = salsa_struct.field_indices();
let tracked_indices = salsa_struct.tracked_indices();
let untracked_indices = salsa_struct.untracked_indices();
let num_fields = salsa_struct.num_fields();
let field_options = salsa_struct.field_options();
let tracked_options = salsa_struct.tracked_options();
let untracked_options = salsa_struct.untracked_options();
let field_tys = salsa_struct.field_tys();
let tracked_tys = salsa_struct.tracked_tys();
let untracked_tys = salsa_struct.untracked_tys();
let num_fields = salsa_struct.num_fields();
let generate_debug_impl = salsa_struct.generate_debug_impl();
let zalsa = self.hygiene.ident("zalsa");

View file

@ -79,7 +79,6 @@ pub enum Op {
pub struct Function<'db> {
pub name: FunctionId<'db>,
#[tracked]
name_span: Span<'db>,
#[tracked]

View file

@ -26,7 +26,6 @@ pub mod tracked_field;
pub trait Configuration: Sized + 'static {
const DEBUG_NAME: &'static str;
const FIELD_DEBUG_NAMES: &'static [&'static str];
const TRACKED_FIELD_DEBUG_NAMES: &'static [&'static str];
/// A (possibly empty) tuple of the fields for this struct.
type Fields<'db>: Send + Sync;
@ -109,7 +108,9 @@ impl<C: Configuration> Jar for JarImpl<C> {
let struct_ingredient = <IngredientImpl<C>>::new(struct_index);
std::iter::once(Box::new(struct_ingredient) as _)
.chain((0..C::TRACKED_FIELD_DEBUG_NAMES.len()).map(|field_index| {
// Note that we create ingredients for untracked fields as well, in order to
// keep field indices relative to the entire struct.
.chain((0..C::FIELD_DEBUG_NAMES.len()).map(|field_index| {
Box::new(<FieldIngredientImpl<C>>::new(struct_index, field_index)) as _
}))
.collect()

View file

@ -0,0 +1,68 @@
mod common;
use salsa::{Database, Setter};
// A tracked struct with mixed tracked and untracked fields to ensure
// the correct field indices are used when tracking dependencies.
#[salsa::tracked]
struct Tracked<'db> {
untracked_1: usize,
#[tracked]
tracked_1: usize,
untracked_2: usize,
untracked_3: usize,
#[tracked]
tracked_2: usize,
untracked_4: usize,
}
#[salsa::input]
struct MyInput {
field1: usize,
field2: usize,
}
#[salsa::tracked]
fn intermediate(db: &dyn salsa::Database, input: MyInput) -> Tracked<'_> {
Tracked::new(db, 0, input.field1(db), 0, 0, input.field2(db), 0)
}
#[salsa::tracked]
fn accumulate(db: &dyn salsa::Database, input: MyInput) -> (usize, usize) {
let tracked = intermediate(db, input);
let one = read_tracked_1(db, tracked);
let two = read_tracked_2(db, tracked);
(one, two)
}
#[salsa::tracked]
fn read_tracked_1<'db>(db: &'db dyn Database, tracked: Tracked<'db>) -> usize {
tracked.tracked_1(db)
}
#[salsa::tracked]
fn read_tracked_2<'db>(db: &'db dyn Database, tracked: Tracked<'db>) -> usize {
tracked.tracked_2(db)
}
#[test]
fn execute() {
let mut db = salsa::DatabaseImpl::default();
let input = MyInput::new(&db, 1, 1);
assert_eq!(accumulate(&db, input), (1, 1));
// Should only re-execute `read_tracked_1`.
input.set_field1(&mut db).to(2);
assert_eq!(accumulate(&db, input), (2, 1));
// Should only re-execute `read_tracked_2`.
input.set_field2(&mut db).to(2);
assert_eq!(accumulate(&db, input), (2, 2));
}