-
Notifications
You must be signed in to change notification settings - Fork 13.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add tombstone checks to make sure incr-comp reads aren't dirtied #40362
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,10 @@ | |
|
||
use hir::def_id::DefId; | ||
use rustc_data_structures::fx::FxHashMap; | ||
|
||
#[cfg(debug_assertions)] | ||
use rustc_data_structures::fx::FxHashSet; | ||
|
||
use std::cell::RefCell; | ||
use std::collections::hash_map::Entry; | ||
use std::ops::Index; | ||
|
@@ -26,6 +30,8 @@ pub struct DepTrackingMap<M: DepTrackingMapConfig> { | |
phantom: PhantomData<M>, | ||
graph: DepGraph, | ||
map: FxHashMap<M::Key, M::Value>, | ||
#[cfg(debug_assertions)] | ||
tombstones: RefCell<FxHashSet<M::Key>>, | ||
} | ||
|
||
pub trait DepTrackingMapConfig { | ||
|
@@ -35,11 +41,22 @@ pub trait DepTrackingMapConfig { | |
} | ||
|
||
impl<M: DepTrackingMapConfig> DepTrackingMap<M> { | ||
#[cfg(debug_assertions)] | ||
pub fn new(graph: DepGraph) -> DepTrackingMap<M> { | ||
DepTrackingMap { | ||
phantom: PhantomData, | ||
graph: graph, | ||
map: FxHashMap(), | ||
tombstones: RefCell::new(FxHashSet()), | ||
} | ||
} | ||
|
||
#[cfg(not(debug_assertions))] | ||
pub fn new(graph: DepGraph) -> DepTrackingMap<M> { | ||
DepTrackingMap { | ||
phantom: PhantomData, | ||
graph: graph, | ||
map: FxHashMap() | ||
map: FxHashMap(), | ||
} | ||
} | ||
|
||
|
@@ -59,13 +76,31 @@ impl<M: DepTrackingMapConfig> DepTrackingMap<M> { | |
|
||
pub fn get(&self, k: &M::Key) -> Option<&M::Value> { | ||
self.read(k); | ||
self.map.get(k) | ||
let result = self.map.get(k); | ||
|
||
#[cfg(debug_assertions)] | ||
{ | ||
if result.is_none() { | ||
self.tombstones.borrow_mut().insert(k.clone()); | ||
} | ||
} | ||
|
||
result | ||
} | ||
|
||
pub fn insert(&mut self, k: M::Key, v: M::Value) { | ||
self.write(&k); | ||
|
||
// If we ever read a `None` value for this key, we do not want | ||
// to permit a later write to it. The only valid use case for | ||
// this is the memoization pattern: for that, use `memoize()` | ||
// below | ||
#[cfg(debug_assertions)] | ||
assert!(!self.tombstones.borrow().contains(&k), | ||
"inserting to `{0:?}` after reading from `{0:?}`", | ||
M::to_dep_node(&k)); | ||
let old_value = self.map.insert(k, v); | ||
assert!(old_value.is_none()); | ||
assert!(old_value.is_none(), "inserted value twice"); | ||
} | ||
|
||
pub fn entry(&mut self, k: M::Key) -> Entry<M::Key, M::Value> { | ||
|
@@ -75,7 +110,13 @@ impl<M: DepTrackingMapConfig> DepTrackingMap<M> { | |
|
||
pub fn contains_key(&self, k: &M::Key) -> bool { | ||
self.read(k); | ||
self.map.contains_key(k) | ||
if self.map.contains_key(k) { | ||
true | ||
} else { | ||
#[cfg(debug_assertions)] | ||
self.tombstones.borrow_mut().insert(k.clone()); | ||
false | ||
} | ||
} | ||
|
||
pub fn keys(&self) -> Vec<M::Key> { | ||
|
@@ -125,7 +166,7 @@ impl<M: DepTrackingMapConfig> MemoizationMap for RefCell<DepTrackingMap<M>> { | |
/// let item_def_id = ccx.tcx.hir.local_def_id(it.id); | ||
/// ccx.tcx.item_types.memoized(item_def_id, || { | ||
/// ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); // (*) | ||
/// compute_type_of_item(ccx, item) | ||
/// Some(compute_type_of_item(ccx, item)) | ||
/// }); | ||
/// } | ||
/// ``` | ||
|
@@ -134,7 +175,7 @@ impl<M: DepTrackingMapConfig> MemoizationMap for RefCell<DepTrackingMap<M>> { | |
/// accesses the body of the item `item`, so we register a read | ||
/// from `Hir(item_def_id)`. | ||
fn memoize<OP>(&self, key: M::Key, op: OP) -> M::Value | ||
where OP: FnOnce() -> M::Value | ||
where OP: FnOnce() -> Option<M::Value> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it looks to me like there is no longer a need for this part of the diff -- that is, we can make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a little hard to see because of how the diff reads on Github, but this call to memoize has a closure that returns There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I thought @eddyb refactored that use. I wonder if we want to do so in this PR instead. Right now what happens is that we request impl Foo {
type A;
type X;
fn b() { ... }
} To do this, we go up to the enclosing impl (the one shown there) and then iterate over its list of items. Right now, since we are iterating anyway, we would compute the associated item info for We could thus change this code to not generate N outputs but just find the one item we actually want. It would be O(N^2) but N here is typically very small. And we do plan to rework things over time that would mitigate -- for example, moving to a newer system where we wouldn't have to be so careful about our dependencies. The whole motivation here was to only read from the impl when computing this data, and not the items themselves. This way, if the item X changes, we don't have to consider the value as dirty (the parts of X that would affect this value are copied into the impl and hashed there too). But we want to move to a system where we would recompute this value (which is cheap) and then see that, indeed, it has not changed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The best fix IMO is to move |
||
{ | ||
let graph; | ||
{ | ||
|
@@ -147,9 +188,18 @@ impl<M: DepTrackingMapConfig> MemoizationMap for RefCell<DepTrackingMap<M>> { | |
} | ||
|
||
let _task = graph.in_task(M::to_dep_node(&key)); | ||
let result = op(); | ||
self.borrow_mut().map.insert(key, result.clone()); | ||
result | ||
if let Some(result) = op() { | ||
let old_value = self.borrow_mut().map.insert(key, result.clone()); | ||
assert!(old_value.is_none()); | ||
result | ||
} else { | ||
self.borrow().map | ||
.get(&key) | ||
.unwrap_or_else(|| bug!( | ||
"memoize closure failed to write to {:?}", M::to_dep_node(&key) | ||
)) | ||
.clone() | ||
} | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The following lines should probably be wrapped in a block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I misread. But a
debug_assert()
would be more concise.