Skip to content

Commit 4b1dfbd

Browse files
committed
Auto merge of #40308 - nikomatsakis:incr-comp-isolate-task, r=mw
first pass at isolating dep-graph tasks This intentionally leaves `DepGraph::in_task()`, the more common form, alone. Eventually all uses of `DepGraph::in_task()` should be ported to `with_task()`, but I wanted to start with a smaller subset. I also used `AssertDepGraphSafe` on the closures that are found in trans. This is because the types there are non-trivial and I wanted to lay down the mechanism and come back to the more subtle cases. The current approach taken in this PR has a downside: it is necessary to manually "reify" fn types into fn pointers when starting a task, like so: dep_graph.with_task(..., task_fn as fn(_)) this is because `with_task` takes some type `T` that implements `DepGraphTask` rather than taking a `fn()` type directly. *This* is so that we can accept closure and also so that we can accept fns with multiple arities. I am not sure this is the right approach. Originally I wanted to use closures bound by an auto trait, but that approach has some limitations: - the trait cannot have a `read()` method; since the current method is unused, that may not be a problem. - more importantly, we would want the auto trait to be "undefined" for all types *by default* -- that is, this use case doesn't really fit the typical auto trait scenario. For example, imagine that there is a `u32` loaded out of a `hir::Node` -- we don't really want to be passing that `u32` into the task!
2 parents 5d0be0d + 4d5441f commit 4b1dfbd

File tree

10 files changed

+169
-48
lines changed

10 files changed

+169
-48
lines changed

src/librustc/dep_graph/graph.rs

+31-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use std::sync::Arc;
1818
use super::dep_node::{DepNode, WorkProductId};
1919
use super::query::DepGraphQuery;
2020
use super::raii;
21+
use super::safe::DepGraphSafe;
2122
use super::thread::{DepGraphThreadData, DepMessage};
2223

2324
#[derive(Clone)]
@@ -76,11 +77,38 @@ impl DepGraph {
7677
op()
7778
}
7879

79-
pub fn with_task<OP,R>(&self, key: DepNode<DefId>, op: OP) -> R
80-
where OP: FnOnce() -> R
80+
/// Starts a new dep-graph task. Dep-graph tasks are specified
81+
/// using a free function (`task`) and **not** a closure -- this
82+
/// is intentional because we want to exercise tight control over
83+
/// what state they have access to. In particular, we want to
84+
/// prevent implicit 'leaks' of tracked state into the task (which
85+
/// could then be read without generating correct edges in the
86+
/// dep-graph -- see the [README] for more details on the
87+
/// dep-graph). To this end, the task function gets exactly two
88+
/// pieces of state: the context `cx` and an argument `arg`. Both
89+
/// of these bits of state must be of some type that implements
90+
/// `DepGraphSafe` and hence does not leak.
91+
///
92+
/// The choice of two arguments is not fundamental. One argument
93+
/// would work just as well, since multiple values can be
94+
/// collected using tuples. However, using two arguments works out
95+
/// to be quite convenient, since it is common to need a context
96+
/// (`cx`) and some argument (e.g., a `DefId` identifying what
97+
/// item to process).
98+
///
99+
/// For cases where you need some other number of arguments:
100+
///
101+
/// - If you only need one argument, just use `()` for the `arg`
102+
/// parameter.
103+
/// - If you need 3+ arguments, use a tuple for the
104+
/// `arg` parameter.
105+
///
106+
/// [README]: README.md
107+
pub fn with_task<C, A, R>(&self, key: DepNode<DefId>, cx: C, arg: A, task: fn(C, A) -> R) -> R
108+
where C: DepGraphSafe, A: DepGraphSafe
81109
{
82110
let _task = self.in_task(key);
83-
op()
111+
task(cx, arg)
84112
}
85113

86114
pub fn read(&self, v: DepNode<DefId>) {

src/librustc/dep_graph/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod edges;
1515
mod graph;
1616
mod query;
1717
mod raii;
18+
mod safe;
1819
mod shadow;
1920
mod thread;
2021
mod visit;
@@ -25,6 +26,8 @@ pub use self::dep_node::WorkProductId;
2526
pub use self::graph::DepGraph;
2627
pub use self::graph::WorkProduct;
2728
pub use self::query::DepGraphQuery;
29+
pub use self::safe::AssertDepGraphSafe;
30+
pub use self::safe::DepGraphSafe;
2831
pub use self::visit::visit_all_bodies_in_krate;
2932
pub use self::visit::visit_all_item_likes_in_krate;
3033
pub use self::raii::DepTask;

src/librustc/dep_graph/safe.rs

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use hir::BodyId;
12+
use hir::def_id::DefId;
13+
use syntax::ast::NodeId;
14+
use ty::TyCtxt;
15+
16+
/// The `DepGraphSafe` trait is used to specify what kinds of values
17+
/// are safe to "leak" into a task. The idea is that this should be
18+
/// only be implemented for things like the tcx as well as various id
19+
/// types, which will create reads in the dep-graph whenever the trait
20+
/// loads anything that might depend on the input program.
21+
pub trait DepGraphSafe {
22+
}
23+
24+
/// A `BodyId` on its own doesn't give access to any particular state.
25+
/// You must fetch the state from the various maps or generate
26+
/// on-demand queries, all of which create reads.
27+
impl DepGraphSafe for BodyId {
28+
}
29+
30+
/// A `NodeId` on its own doesn't give access to any particular state.
31+
/// You must fetch the state from the various maps or generate
32+
/// on-demand queries, all of which create reads.
33+
impl DepGraphSafe for NodeId {
34+
}
35+
36+
/// A `DefId` on its own doesn't give access to any particular state.
37+
/// You must fetch the state from the various maps or generate
38+
/// on-demand queries, all of which create reads.
39+
impl DepGraphSafe for DefId {
40+
}
41+
42+
/// The type context itself can be used to access all kinds of tracked
43+
/// state, but those accesses should always generate read events.
44+
impl<'a, 'gcx, 'tcx> DepGraphSafe for TyCtxt<'a, 'gcx, 'tcx> {
45+
}
46+
47+
/// Tuples make it easy to build up state.
48+
impl<A, B> DepGraphSafe for (A, B)
49+
where A: DepGraphSafe, B: DepGraphSafe
50+
{
51+
}
52+
53+
/// No data here! :)
54+
impl DepGraphSafe for () {
55+
}
56+
57+
/// A convenient override that lets you pass arbitrary state into a
58+
/// task. Every use should be accompanied by a comment explaining why
59+
/// it makes sense (or how it could be refactored away in the future).
60+
pub struct AssertDepGraphSafe<T>(pub T);
61+
62+
impl<T> DepGraphSafe for AssertDepGraphSafe<T> {
63+
}

src/librustc_borrowck/borrowck/mod.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,16 @@ pub struct LoanDataFlowOperator;
6161
pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator>;
6262

6363
pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
64-
tcx.dep_graph.with_task(DepNode::BorrowCheckKrate, || {
64+
tcx.dep_graph.with_task(DepNode::BorrowCheckKrate, tcx, (), check_crate_task);
65+
66+
fn check_crate_task<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (): ()) {
6567
tcx.visit_all_bodies_in_krate(|body_owner_def_id, body_id| {
66-
tcx.dep_graph.with_task(DepNode::BorrowCheck(body_owner_def_id), || {
67-
borrowck_fn(tcx, body_id);
68-
});
68+
tcx.dep_graph.with_task(DepNode::BorrowCheck(body_owner_def_id),
69+
tcx,
70+
body_id,
71+
borrowck_fn);
6972
});
70-
});
73+
}
7174
}
7275

7376
/// Collection of conclusions determined via borrow checker analyses.

src/librustc_incremental/persist/load.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,11 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
192192
clean_work_products.insert(wp.clone());
193193
}
194194

195-
tcx.dep_graph.with_task(n, || ()); // create the node with no inputs
195+
tcx.dep_graph.with_task(n, (), (), create_node);
196+
197+
fn create_node((): (), (): ()) {
198+
// just create the node with no inputs
199+
}
196200
}
197201
}
198202

src/librustc_mir/mir_map.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,13 @@ use std::cell::RefCell;
3838
use std::mem;
3939

4040
pub fn build_mir_for_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
41-
tcx.dep_graph.with_task(DepNode::MirKrate, || {
41+
tcx.dep_graph.with_task(DepNode::MirKrate, tcx, (), build_mir_for_crate_task);
42+
43+
fn build_mir_for_crate_task<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (): ()) {
4244
tcx.visit_all_bodies_in_krate(|body_owner_def_id, _body_id| {
4345
tcx.item_mir(body_owner_def_id);
4446
});
45-
});
47+
}
4648
}
4749

4850
pub fn provide(providers: &mut Providers) {

src/librustc_trans/base.rs

+29-10
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use rustc::mir::tcx::LvalueTy;
4141
use rustc::traits;
4242
use rustc::ty::{self, Ty, TyCtxt};
4343
use rustc::ty::adjustment::CustomCoerceUnsized;
44-
use rustc::dep_graph::{DepNode, WorkProduct};
44+
use rustc::dep_graph::{AssertDepGraphSafe, DepNode, WorkProduct};
4545
use rustc::hir::map as hir_map;
4646
use rustc::util::common::time;
4747
use session::config::{self, NoDebugInfo};
@@ -1211,21 +1211,40 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
12111211

12121212
// Instantiate translation items without filling out definitions yet...
12131213
for ccx in crate_context_list.iter_need_trans() {
1214-
let cgu = ccx.codegen_unit();
1215-
let trans_items = cgu.items_in_deterministic_order(tcx, &symbol_map);
1216-
1217-
tcx.dep_graph.with_task(cgu.work_product_dep_node(), || {
1214+
let dep_node = ccx.codegen_unit().work_product_dep_node();
1215+
tcx.dep_graph.with_task(dep_node,
1216+
ccx,
1217+
AssertDepGraphSafe(symbol_map.clone()),
1218+
trans_decl_task);
1219+
1220+
fn trans_decl_task<'a, 'tcx>(ccx: CrateContext<'a, 'tcx>,
1221+
symbol_map: AssertDepGraphSafe<Rc<SymbolMap<'tcx>>>) {
1222+
// FIXME(#40304): Instead of this, the symbol-map should be an
1223+
// on-demand thing that we compute.
1224+
let AssertDepGraphSafe(symbol_map) = symbol_map;
1225+
let cgu = ccx.codegen_unit();
1226+
let trans_items = cgu.items_in_deterministic_order(ccx.tcx(), &symbol_map);
12181227
for (trans_item, linkage) in trans_items {
12191228
trans_item.predefine(&ccx, linkage);
12201229
}
1221-
});
1230+
}
12221231
}
12231232

12241233
// ... and now that we have everything pre-defined, fill out those definitions.
12251234
for ccx in crate_context_list.iter_need_trans() {
1226-
let cgu = ccx.codegen_unit();
1227-
let trans_items = cgu.items_in_deterministic_order(tcx, &symbol_map);
1228-
tcx.dep_graph.with_task(cgu.work_product_dep_node(), || {
1235+
let dep_node = ccx.codegen_unit().work_product_dep_node();
1236+
tcx.dep_graph.with_task(dep_node,
1237+
ccx,
1238+
AssertDepGraphSafe(symbol_map.clone()),
1239+
trans_def_task);
1240+
1241+
fn trans_def_task<'a, 'tcx>(ccx: CrateContext<'a, 'tcx>,
1242+
symbol_map: AssertDepGraphSafe<Rc<SymbolMap<'tcx>>>) {
1243+
// FIXME(#40304): Instead of this, the symbol-map should be an
1244+
// on-demand thing that we compute.
1245+
let AssertDepGraphSafe(symbol_map) = symbol_map;
1246+
let cgu = ccx.codegen_unit();
1247+
let trans_items = cgu.items_in_deterministic_order(ccx.tcx(), &symbol_map);
12291248
for (trans_item, _) in trans_items {
12301249
trans_item.define(&ccx);
12311250
}
@@ -1247,7 +1266,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
12471266
if ccx.sess().opts.debuginfo != NoDebugInfo {
12481267
debuginfo::finalize(&ccx);
12491268
}
1250-
});
1269+
}
12511270
}
12521271

12531272
symbol_names_test::report_symbol_names(&shared_ccx);

src/librustc_trans/context.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
use llvm;
1212
use llvm::{ContextRef, ModuleRef, ValueRef};
13-
use rustc::dep_graph::{DepGraph, DepNode, DepTrackingMap, DepTrackingMapConfig, WorkProduct};
13+
use rustc::dep_graph::{DepGraph, DepGraphSafe, DepNode, DepTrackingMap,
14+
DepTrackingMapConfig, WorkProduct};
1415
use middle::cstore::LinkMeta;
1516
use rustc::hir;
1617
use rustc::hir::def::ExportMap;
@@ -274,6 +275,9 @@ pub struct CrateContext<'a, 'tcx: 'a> {
274275
index: usize,
275276
}
276277

278+
impl<'a, 'tcx> DepGraphSafe for CrateContext<'a, 'tcx> {
279+
}
280+
277281
pub struct CrateContextIterator<'a, 'tcx: 'a> {
278282
shared: &'a SharedCrateContext<'a, 'tcx>,
279283
local_ccxs: &'a [LocalCrateContext<'tcx>],

src/librustc_typeck/check/mod.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -539,13 +539,15 @@ pub fn check_item_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> CompileResult
539539
}
540540

541541
pub fn check_item_bodies<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> CompileResult {
542-
tcx.sess.track_errors(|| {
543-
tcx.dep_graph.with_task(DepNode::TypeckBodiesKrate, || {
544-
tcx.visit_all_bodies_in_krate(|body_owner_def_id, _body_id| {
545-
tcx.item_tables(body_owner_def_id);
546-
});
542+
return tcx.sess.track_errors(|| {
543+
tcx.dep_graph.with_task(DepNode::TypeckBodiesKrate, tcx, (), check_item_bodies_task);
544+
});
545+
546+
fn check_item_bodies_task<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (): ()) {
547+
tcx.visit_all_bodies_in_krate(|body_owner_def_id, _body_id| {
548+
tcx.item_tables(body_owner_def_id);
547549
});
548-
})
550+
}
549551
}
550552

551553
pub fn provide(providers: &mut Providers) {

src/librustc_typeck/collect.rs

+13-20
Original file line numberDiff line numberDiff line change
@@ -165,15 +165,10 @@ impl<'a, 'tcx> CollectItemTypesVisitor<'a, 'tcx> {
165165
/// 4. This is added by the code in `visit_expr` when we write to `item_types`.
166166
/// 5. This is added by the code in `convert_item` when we write to `item_types`;
167167
/// note that this write occurs inside the `CollectItemSig` task.
168-
/// 6. Added by explicit `read` below
169-
fn with_collect_item_sig<OP>(&self, id: ast::NodeId, op: OP)
170-
where OP: FnOnce()
171-
{
168+
/// 6. Added by reads from within `op`.
169+
fn with_collect_item_sig(&self, id: ast::NodeId, op: fn(TyCtxt<'a, 'tcx, 'tcx>, ast::NodeId)) {
172170
let def_id = self.tcx.hir.local_def_id(id);
173-
self.tcx.dep_graph.with_task(DepNode::CollectItemSig(def_id), || {
174-
self.tcx.hir.read(id);
175-
op();
176-
});
171+
self.tcx.dep_graph.with_task(DepNode::CollectItemSig(def_id), self.tcx, id, op);
177172
}
178173
}
179174

@@ -183,7 +178,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'a, 'tcx> {
183178
}
184179

185180
fn visit_item(&mut self, item: &'tcx hir::Item) {
186-
self.with_collect_item_sig(item.id, || convert_item(self.tcx, item));
181+
self.with_collect_item_sig(item.id, convert_item);
187182
intravisit::walk_item(self, item);
188183
}
189184

@@ -216,16 +211,12 @@ impl<'a, 'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'a, 'tcx> {
216211
}
217212

218213
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
219-
self.with_collect_item_sig(trait_item.id, || {
220-
convert_trait_item(self.tcx, trait_item)
221-
});
214+
self.with_collect_item_sig(trait_item.id, convert_trait_item);
222215
intravisit::walk_trait_item(self, trait_item);
223216
}
224217

225218
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
226-
self.with_collect_item_sig(impl_item.id, || {
227-
convert_impl_item(self.tcx, impl_item)
228-
});
219+
self.with_collect_item_sig(impl_item.id, convert_impl_item);
229220
intravisit::walk_impl_item(self, impl_item);
230221
}
231222
}
@@ -493,9 +484,10 @@ fn ensure_no_ty_param_bounds(tcx: TyCtxt,
493484
}
494485
}
495486

496-
fn convert_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &hir::Item) {
487+
fn convert_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_id: ast::NodeId) {
488+
let it = tcx.hir.expect_item(item_id);
497489
debug!("convert: item {} with id {}", it.name, it.id);
498-
let def_id = tcx.hir.local_def_id(it.id);
490+
let def_id = tcx.hir.local_def_id(item_id);
499491
match it.node {
500492
// These don't define types.
501493
hir::ItemExternCrate(_) | hir::ItemUse(..) | hir::ItemMod(_) => {
@@ -560,7 +552,8 @@ fn convert_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &hir::Item) {
560552
}
561553
}
562554

563-
fn convert_trait_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, trait_item: &hir::TraitItem) {
555+
fn convert_trait_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, trait_item_id: ast::NodeId) {
556+
let trait_item = tcx.hir.expect_trait_item(trait_item_id);
564557
let def_id = tcx.hir.local_def_id(trait_item.id);
565558
tcx.item_generics(def_id);
566559

@@ -577,8 +570,8 @@ fn convert_trait_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, trait_item: &hir::T
577570
tcx.item_predicates(def_id);
578571
}
579572

580-
fn convert_impl_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_item: &hir::ImplItem) {
581-
let def_id = tcx.hir.local_def_id(impl_item.id);
573+
fn convert_impl_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_item_id: ast::NodeId) {
574+
let def_id = tcx.hir.local_def_id(impl_item_id);
582575
tcx.item_generics(def_id);
583576
tcx.item_type(def_id);
584577
tcx.item_predicates(def_id);

0 commit comments

Comments
 (0)