Skip to content

Commit cf9efae

Browse files
Make interned's last_interned_at equal Revision::MAX if they are interned outside a query (#804)
There is an assert that `last_interned_at >= last_changed_revision`, and it can fail without this, see the added test.
1 parent 05b4fad commit cf9efae

File tree

3 files changed

+48
-5
lines changed

3 files changed

+48
-5
lines changed

src/interned.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -282,11 +282,12 @@ where
282282
let table = zalsa.table();
283283

284284
// Record the durability of the current query on the interned value.
285-
let durability = zalsa_local
285+
let (durability, last_interned_at) = zalsa_local
286286
.active_query()
287-
.map(|(_, stamp)| stamp.durability)
287+
.map(|(_, stamp)| (stamp.durability, current_revision))
288288
// If there is no active query this durability does not actually matter.
289-
.unwrap_or(Durability::MAX);
289+
// `last_interned_at` needs to be `Revision::MAX`, see the intern_access_in_different_revision test.
290+
.unwrap_or((Durability::MAX, Revision::max()));
290291

291292
let id = zalsa_local.allocate(table, self.ingredient_index, |id| Value::<C> {
292293
fields: unsafe { self.to_internal_data(assemble(id, key)) },
@@ -295,7 +296,7 @@ where
295296
durability: AtomicU8::new(durability.as_u8()),
296297
// Record the revision we are interning in.
297298
first_interned_at: current_revision,
298-
last_interned_at: AtomicRevision::from(current_revision),
299+
last_interned_at: AtomicRevision::from(last_interned_at),
299300
});
300301

301302
let value = table.get::<Value<C>>(id);
@@ -391,7 +392,11 @@ where
391392

392393
// The slot is valid in this revision but we have to sync the value's revision.
393394
let current_revision = zalsa.current_revision();
394-
value.last_interned_at.store(current_revision);
395+
// No `if` to be branchless.
396+
value.last_interned_at.store(std::cmp::max(
397+
current_revision,
398+
value.last_interned_at.load(),
399+
));
395400

396401
db.salsa_event(&|| {
397402
Event::new(EventKind::DidReinternValue {

src/revision.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,34 @@ pub struct Revision {
1717
}
1818

1919
impl Revision {
20+
#[inline]
21+
pub(crate) fn max() -> Self {
22+
Self::from(usize::MAX)
23+
}
24+
25+
#[inline]
2026
pub(crate) fn start() -> Self {
2127
Self::from(START)
2228
}
2329

30+
#[inline]
2431
pub(crate) fn from(g: usize) -> Self {
2532
Self {
2633
generation: NonZeroUsize::new(g).unwrap(),
2734
}
2835
}
2936

37+
#[inline]
3038
pub(crate) fn from_opt(g: usize) -> Option<Self> {
3139
NonZeroUsize::new(g).map(|generation| Self { generation })
3240
}
3341

42+
#[inline]
3443
pub(crate) fn next(self) -> Revision {
3544
Self::from(self.generation.get() + 1)
3645
}
3746

47+
#[inline]
3848
fn as_usize(self) -> usize {
3949
self.generation.get()
4050
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use salsa::{Durability, Setter};
2+
3+
#[salsa::interned(no_lifetime)]
4+
struct Interned {
5+
field: u32,
6+
}
7+
8+
#[salsa::input]
9+
struct Input {
10+
field: i32,
11+
}
12+
13+
#[test]
14+
fn the_test() {
15+
let mut db = salsa::DatabaseImpl::default();
16+
let input = Input::builder(-123456)
17+
.field_durability(Durability::HIGH)
18+
.new(&db);
19+
// Create an intern in an early revision.
20+
let interned = Interned::new(&db, 0xDEADBEEF);
21+
// Trigger a new revision.
22+
input
23+
.set_field(&mut db)
24+
.with_durability(Durability::HIGH)
25+
.to(123456);
26+
// Read the interned value
27+
let _ = interned.field(&db);
28+
}

0 commit comments

Comments
 (0)