restructure CollectItem dep-node to separate fn sigs from bodies

Setup two tasks, one of which only processes the signatures, in order to
isolate the typeck entries for signatures from those for bodies.

Fixes #36078
Fixes #37720
This commit is contained in:
Niko Matsakis 2016-11-21 17:12:35 -05:00 committed by Florian Diebold
parent f75c8a98dd
commit 688946d671
5 changed files with 62 additions and 16 deletions

View File

@ -62,6 +62,7 @@ pub enum DepNode<D: Clone + Debug> {
PluginRegistrar,
StabilityIndex,
CollectItem(D),
CollectItemSig(D),
Coherence,
EffectCheck,
Liveness,
@ -206,6 +207,7 @@ impl<D: Clone + Debug> DepNode<D> {
HirBody(ref d) => op(d).map(HirBody),
MetaData(ref d) => op(d).map(MetaData),
CollectItem(ref d) => op(d).map(CollectItem),
CollectItemSig(ref d) => op(d).map(CollectItemSig),
CoherenceCheckImpl(ref d) => op(d).map(CoherenceCheckImpl),
CoherenceOverlapCheck(ref d) => op(d).map(CoherenceOverlapCheck),
CoherenceOverlapCheckSpecial(ref d) => op(d).map(CoherenceOverlapCheckSpecial),

View File

@ -128,13 +128,62 @@ struct CollectItemTypesVisitor<'a, 'tcx: 'a> {
ccx: &'a CrateCtxt<'a, 'tcx>
}
impl<'a, 'tcx> CollectItemTypesVisitor<'a, 'tcx> {
/// Collect item types is structured into two tasks. The outer
/// task, `CollectItem`, walks the entire content of an item-like
/// thing, including its body. It also spawns an inner task,
/// `CollectItemSig`, which walks only the signature. This inner
/// task is the one that writes the item-type into the various
/// maps. This setup ensures that the item body is never
/// accessible to the task that computes its signature, so that
/// changes to the body don't affect the signature.
///
/// Consider an example function `foo` that also has a closure in its body:
///
/// ```
/// fn foo(<sig>) {
/// ...
/// let bar = || ...; // we'll label this closure as "bar" below
/// }
/// ```
///
/// This results in a dep-graph like so. I've labeled the edges to
/// document where they arise.
///
/// ```
/// [HirBody(foo)] -2--> [CollectItem(foo)] -4-> [ItemSignature(bar)]
/// ^ ^
/// 1 3
/// [Hir(foo)] -----------+-6-> [CollectItemSig(foo)] -5-> [ItemSignature(foo)]
/// ```
///
/// 1. This is added by the `visit_all_item_likes_in_krate`.
/// 2. This is added when we fetch the item body.
/// 3. This is added because `CollectItem` launches `CollectItemSig`.
/// - it is arguably false; if we refactor the `with_task` system;
/// we could get probably rid of it, but it is also harmless enough.
/// 4. This is added by the code in `visit_expr` when we write to `item_types`.
/// 5. This is added by the code in `convert_item` when we write to `item_types`;
/// note that this write occurs inside the `CollectItemSig` task.
/// 6. Added by explicit `read` below
fn with_collect_item_sig<OP>(&self, id: ast::NodeId, op: OP)
where OP: FnOnce()
{
let def_id = self.ccx.tcx.map.local_def_id(id);
self.ccx.tcx.dep_graph.with_task(DepNode::CollectItemSig(def_id), || {
self.ccx.tcx.map.read(id);
op();
});
}
}
impl<'a, 'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'a, 'tcx> {
fn nested_visit_map(&mut self) -> Option<(&hir::map::Map<'tcx>, NestedVisitMode)> {
Some((&self.ccx.tcx.map, NestedVisitMode::OnlyBodies))
}
fn visit_item(&mut self, item: &'tcx hir::Item) {
convert_item(self.ccx, item);
self.with_collect_item_sig(item.id, || convert_item(self.ccx, item));
intravisit::walk_item(self, item);
}
@ -156,7 +205,9 @@ impl<'a, 'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'a, 'tcx> {
}
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
convert_impl_item(self.ccx, impl_item);
self.with_collect_item_sig(impl_item.id, || {
convert_impl_item(self.ccx, impl_item)
});
intravisit::walk_impl_item(self, impl_item);
}
}

View File

@ -23,9 +23,8 @@
#![rustc_partition_reused(module="struct_point-fn_write_field", cfg="rpass2")]
#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="rpass2")]
// FIXME(#37720) these two should be reused, but data gets entangled across crates
#![rustc_partition_translated(module="struct_point-fn_calls_methods_in_same_impl", cfg="rpass2")]
#![rustc_partition_translated(module="struct_point-fn_calls_methods_in_another_impl", cfg="rpass2")]
#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="rpass2")]
#![rustc_partition_reused(module="struct_point-fn_calls_methods_in_another_impl", cfg="rpass2")]
extern crate point;
@ -33,8 +32,7 @@ extern crate point;
mod fn_calls_methods_in_same_impl {
use point::Point;
// FIXME(#37720) data gets entangled across crates
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
pub fn check() {
let x = Point { x: 2.0, y: 2.0 };
x.distance_from_origin();
@ -45,8 +43,7 @@ mod fn_calls_methods_in_same_impl {
mod fn_calls_methods_in_another_impl {
use point::Point;
// FIXME(#37720) data gets entangled across crates
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
pub fn dirty() {
let mut x = Point { x: 2.0, y: 2.0 };
x.translate(3.0, 3.0);

View File

@ -19,9 +19,7 @@
#![rustc_partition_translated(module="struct_point-point", cfg="rpass2")]
// FIXME(#35078) -- this gets recompiled because we don't separate sig from body
#![rustc_partition_translated(module="struct_point-fn_calls_changed_method", cfg="rpass2")]
#![rustc_partition_reused(module="struct_point-fn_calls_changed_method", cfg="rpass2")]
#![rustc_partition_reused(module="struct_point-fn_calls_another_method", cfg="rpass2")]
#![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="rpass2")]
#![rustc_partition_reused(module="struct_point-fn_read_field", cfg="rpass2")]
@ -52,8 +50,7 @@ mod point {
mod fn_calls_changed_method {
use point::Point;
// FIXME(#35078) -- this gets recompiled because we don't separate sig from body
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
pub fn check() {
let p = Point { x: 2.0, y: 2.0 };
p.distance_from_origin();

View File

@ -31,8 +31,7 @@ mod x {
mod y {
use x;
// FIXME: This should be clean
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
pub fn yyyy() {
x::xxxx();
}