diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs new file mode 100644 index 0000000000000..a0e72b5a07a1f --- /dev/null +++ b/src/librustc/dep_graph/mod.rs @@ -0,0 +1,197 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use middle::def_id::DefId; +use middle::ty; +use rustc_data_structures::fnv::FnvHashMap; +use rustc_data_structures::dependency; +use rustc_front::hir; +use rustc_front::intravisit::Visitor; +use std::ops::Index; +use std::hash::Hash; +use std::marker::PhantomData; +use std::rc::Rc; +use util::common; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum DepNode { + // Represents the `Krate` as a whole (the `hir::Krate` value) (as + // distinct from the krate module). This is basically a hash of + // the entire krate, so if you read from `Krate` (e.g., by calling + // `tcx.map.krate()`), we will have to assume that any change + // means that you need to be recompiled. This is because the + // `Krate` value gives you access to all other items. To avoid + // this fate, do not call `tcx.map.krate()`; instead, prefer + // wrappers like `tcx.visit_all_items_in_krate()`. If there is no + // suitable wrapper, you can use `tcx.dep_graph.ignore()` to gain + // access to the krate, but you must remember to add suitable + // edges yourself for the individual items that you read. + Krate, + + // Represents the HIR node with the given node-id + Hir(DefId), + + // Represents different phases in the compiler. + CollectItem(DefId), + TypeScheme(DefId), + Coherence, + CoherenceOverlapCheck(DefId), + CoherenceOverlapCheckSpecial(DefId), + CoherenceOrphanCheck(DefId), + Variance, + WfCheck(DefId), + TypeckItemType(DefId), + TypeckItemBody(DefId), + Dropck(DefId), + CheckConst(DefId), + Privacy, + IntrinsicCheck(DefId), + MatchCheck(DefId), + MirMapConstruction(DefId), + BorrowCheck(DefId), + RvalueCheck(DefId), + Reachability, + DeadCheck, + StabilityCheck, + LateLintCheck, + IntrinsicUseCheck, + TransLinkMeta, + TransCrateItem(DefId), + TransInlinedItem(DefId), + TransWriteMetadata, + + // Nodes representing bits of computed IR in the tcx. Each of + // these corresponds to a particular table in the tcx. + ImplOrTraitItems(DefId), + TraitItemDefIds(DefId), + ImplTraitRef(DefId), + Tcache(DefId), + TraitDefs(DefId), + AdtDefs(DefId), + Predicates(DefId), + ItemVarianceMap(DefId), +} + +impl dependency::DepNodeId for DepNode { } + +pub type DepGraph = dependency::DepGraph; + +/// A DepTrackingMap offers a subset of the `Map` API and ensures that +/// we make calls to `read` and `write` as appropriate. We key the +/// maps with a unique type for brevity. +pub struct DepTrackingMap { + phantom: PhantomData, + graph: Rc, + map: FnvHashMap, +} + +pub trait DepTrackingMapId { + type Key: Eq + Hash + Clone; + type Value: Clone; + fn to_dep_node(key: &Self::Key) -> DepNode; +} + +impl DepTrackingMap { + pub fn new(graph: Rc) -> DepTrackingMap { + DepTrackingMap { + phantom: PhantomData, + graph: graph, + map: FnvHashMap() + } + } + + /// Registers a (synthetic) read from the key `k`. Usually this + /// is invoked automatically by `get`. + pub fn read(&self, k: &M::Key) { + let dep_node = M::to_dep_node(k); + self.graph.read(dep_node); + } + + /// Registers a (synthetic) write to the key `k`. Usually this is + /// invoked automatically by `insert`. + fn write(&self, k: &M::Key) { + let dep_node = M::to_dep_node(k); + self.graph.write(dep_node); + } + + pub fn get(&self, k: &M::Key) -> Option<&M::Value> { + self.read(k); + self.map.get(k) + } + + pub fn insert(&mut self, k: M::Key, v: M::Value) -> Option { + self.write(&k); + self.map.insert(k, v) + } + + pub fn contains_key(&self, k: &M::Key) -> bool { + self.read(k); + self.map.contains_key(k) + } +} + +impl common::MemoizationMap for DepTrackingMap { + type Key = M::Key; + type Value = M::Value; + fn get(&self, key: &M::Key) -> Option<&M::Value> { + self.get(key) + } + fn insert(&mut self, key: M::Key, value: M::Value) -> Option { + self.insert(key, value) + } +} + +impl<'k, M: DepTrackingMapId> Index<&'k M::Key> for DepTrackingMap { + type Output = M::Value; + + #[inline] + fn index(&self, k: &'k M::Key) -> &M::Value { + self.get(k).unwrap() + } +} + +/// Visit all the items in the krate in some order. When visiting a +/// particular item, first create a dep-node by calling `dep_node_fn` +/// and push that onto the dep-graph stack of tasks, and also create a +/// read edge from the corresponding AST node. This is used in +/// compiler passes to automatically record the item that they are +/// working on. +pub fn visit_all_items_in_krate<'tcx,V,F>(tcx: &ty::ctxt<'tcx>, + mut dep_node_fn: F, + visitor: &mut V) + where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx> +{ + struct TrackingVisitor<'visit, 'tcx: 'visit, F: 'visit, V: 'visit> { + tcx: &'visit ty::ctxt<'tcx>, + dep_node_fn: &'visit mut F, + visitor: &'visit mut V + } + + impl<'visit, 'tcx, F, V> Visitor<'tcx> for TrackingVisitor<'visit, 'tcx, F, V> + where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx> + { + fn visit_item(&mut self, i: &'tcx hir::Item) { + let item_def_id = self.tcx.map.local_def_id(i.id); + let task_id = (self.dep_node_fn)(item_def_id); + debug!("About to start task {:?}", task_id); + let _task = self.tcx.dep_graph.in_task(task_id); + self.tcx.dep_graph.read(DepNode::Hir(item_def_id)); + self.visitor.visit_item(i) + } + } + + let krate = tcx.dep_graph.with_ignore(|| tcx.map.krate()); + let mut tracking_visitor = TrackingVisitor { + tcx: tcx, + dep_node_fn: &mut dep_node_fn, + visitor: visitor + }; + krate.visit_all_items(&mut tracking_visitor) +} diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 0890594f2b116..5ee2a2d20087d 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -90,6 +90,8 @@ pub mod back { pub use rustc_back::svh; } +pub mod dep_graph; + pub mod front { pub mod check_attr; pub mod map; diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 1ed873f0508d5..eaeac7e63e89e 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -25,6 +25,7 @@ //! for all lint attributes. use self::TargetLint::*; +use dep_graph::DepNode; use middle::privacy::AccessLevels; use middle::ty::{self, Ty}; use session::{early_error, Session}; @@ -1025,6 +1026,8 @@ impl LateLintPass for GatherNodeLevels { /// /// Consumes the `lint_store` field of the `Session`. pub fn check_crate(tcx: &ty::ctxt, access_levels: &AccessLevels) { + let _task = tcx.dep_graph.in_task(DepNode::LateLintCheck); + let krate = tcx.map.krate(); let mut cx = LateContext::new(tcx, krate, access_levels); diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 69d8dfc361328..56936a89f311e 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -24,6 +24,7 @@ // - It's not possible to take the address of a static item with unsafe interior. This is enforced // by borrowck::gather_loans +use dep_graph::DepNode; use middle::ty::cast::{CastKind}; use middle::const_eval::{self, ConstEvalErr}; use middle::const_eval::ErrKind::IndexOpFeatureGated; @@ -840,13 +841,12 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp } pub fn check_crate(tcx: &ty::ctxt) { - tcx.map.krate().visit_all_items(&mut CheckCrateVisitor { + tcx.visit_all_items_in_krate(DepNode::CheckConst, &mut CheckCrateVisitor { tcx: tcx, mode: Mode::Var, qualif: ConstQualif::NOT_CONST, rvalue_borrows: NodeMap() }); - tcx.sess.abort_if_errors(); } diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index ba4bdccb20b80..7a2b86f286080 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -12,6 +12,7 @@ pub use self::Constructor::*; use self::Usefulness::*; use self::WitnessPreference::*; +use dep_graph::DepNode; use middle::const_eval::{compare_const_vals, ConstVal}; use middle::const_eval::{eval_const_expr, eval_const_expr_partial}; use middle::const_eval::{const_expr_to_pat, lookup_const_by_id}; @@ -155,7 +156,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> { } pub fn check_crate(tcx: &ty::ctxt) { - tcx.map.krate().visit_all_items(&mut MatchCheckCtxt { + tcx.visit_all_items_in_krate(DepNode::MatchCheck, &mut MatchCheckCtxt { tcx: tcx, param_env: tcx.empty_parameter_environment(), }); diff --git a/src/librustc/middle/check_rvalues.rs b/src/librustc/middle/check_rvalues.rs index 35adeae3e617c..8a3e039ac6e53 100644 --- a/src/librustc/middle/check_rvalues.rs +++ b/src/librustc/middle/check_rvalues.rs @@ -11,21 +11,21 @@ // Checks that all rvalues in a crate have statically known size. check_crate // is the public starting point. +use dep_graph::DepNode; use middle::expr_use_visitor as euv; use middle::infer; use middle::mem_categorization as mc; use middle::ty::ParameterEnvironment; use middle::ty; -use syntax::ast; use rustc_front::hir; -use syntax::codemap::Span; use rustc_front::intravisit; +use syntax::ast; +use syntax::codemap::Span; -pub fn check_crate(tcx: &ty::ctxt, - krate: &hir::Crate) { +pub fn check_crate(tcx: &ty::ctxt) { let mut rvcx = RvalueContext { tcx: tcx }; - krate.visit_all_items(&mut rvcx); + tcx.visit_all_items_in_krate(DepNode::RvalueCheck, &mut rvcx); } struct RvalueContext<'a, 'tcx: 'a> { diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index ec1b447d7111b..1386ef91c70bf 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -12,6 +12,7 @@ // closely. The idea is that all reachable symbols are live, codes called // from live codes are live, and everything else is dead. +use dep_graph::DepNode; use front::map as ast_map; use rustc_front::hir; use rustc_front::intravisit::{self, Visitor}; @@ -590,6 +591,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for DeadVisitor<'a, 'tcx> { } pub fn check_crate(tcx: &ty::ctxt, access_levels: &privacy::AccessLevels) { + let _task = tcx.dep_graph.in_task(DepNode::DeadCheck); let krate = tcx.map.krate(); let live_symbols = find_live(tcx, access_levels, krate); let mut visitor = DeadVisitor { tcx: tcx, live_symbols: live_symbols }; diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 4861f0a6b6436..ed62d87e0614d 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -276,7 +276,7 @@ enum PassArgs { } impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> { - pub fn new(delegate: &'d mut (Delegate<'tcx>), + pub fn new(delegate: &'d mut Delegate<'tcx>, typer: &'t infer::InferCtxt<'a, 'tcx>) -> ExprUseVisitor<'d,'t,'a,'tcx> where 'tcx:'a+'d { diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs index 48d7f44063ec3..f1eed256dd156 100644 --- a/src/librustc/middle/intrinsicck.rs +++ b/src/librustc/middle/intrinsicck.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use dep_graph::DepNode; use middle::def::DefFn; use middle::def_id::DefId; use middle::subst::{Subst, Substs, EnumeratedItems}; @@ -29,7 +30,7 @@ pub fn check_crate(tcx: &ctxt) { dummy_sized_ty: tcx.types.isize, dummy_unsized_ty: tcx.mk_slice(tcx.types.isize), }; - tcx.map.krate().visit_all_items(&mut visitor); + tcx.visit_all_items_in_krate(DepNode::IntrinsicCheck, &mut visitor); } struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> { diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index d146ad2d8003b..738440adf416d 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -15,6 +15,7 @@ // makes all other generics or inline functions that it references // reachable as well. +use dep_graph::DepNode; use front::map as ast_map; use middle::def; use middle::def_id::DefId; @@ -349,6 +350,7 @@ impl<'a, 'v> Visitor<'v> for CollectPrivateImplItemsVisitor<'a> { pub fn find_reachable(tcx: &ty::ctxt, access_levels: &privacy::AccessLevels) -> NodeSet { + let _task = tcx.dep_graph.in_task(DepNode::Reachability); let mut reachable_context = ReachableContext::new(tcx); diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 0d92c3da83c8b..5d848971a476a 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -13,6 +13,7 @@ pub use self::StabilityLevel::*; +use dep_graph::DepNode; use session::Session; use lint; use middle::cstore::{CrateStore, LOCAL_CRATE}; @@ -301,6 +302,7 @@ impl<'tcx> Index<'tcx> { /// features used. pub fn check_unstable_api_usage(tcx: &ty::ctxt) -> FnvHashMap { + let _task = tcx.dep_graph.in_task(DepNode::StabilityCheck); let ref active_lib_features = tcx.sess.features.borrow().declared_lib_features; // Put the active features into a map for quick lookup @@ -314,8 +316,7 @@ pub fn check_unstable_api_usage(tcx: &ty::ctxt) }; intravisit::walk_crate(&mut checker, tcx.map.krate()); - let used_features = checker.used_features; - return used_features; + checker.used_features } struct Checker<'a, 'tcx: 'a> { diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs index cee651743ca86..2dd54adb40011 100644 --- a/src/librustc/middle/ty/context.rs +++ b/src/librustc/middle/ty/context.rs @@ -13,6 +13,7 @@ // FIXME: (@jroesch) @eddyb should remove this when he renames ctxt #![allow(non_camel_case_types)] +use dep_graph::{DepGraph, DepTrackingMap}; use front::map as ast_map; use session::Session; use lint; @@ -33,6 +34,7 @@ use middle::ty::{FreevarMap, GenericPredicates}; use middle::ty::{BareFnTy, InferTy, ParamTy, ProjectionTy, TraitTy}; use middle::ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid}; use middle::ty::TypeVariants::*; +use middle::ty::maps; use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet}; use util::nodemap::FnvHashMap; @@ -224,6 +226,8 @@ pub struct ctxt<'tcx> { region_interner: RefCell>, stability_interner: RefCell>, + pub dep_graph: Rc, + /// Common types, pre-interned for your convenience. pub types: CommonTypes<'tcx>, @@ -245,7 +249,7 @@ pub struct ctxt<'tcx> { pub tables: RefCell>, /// Maps from a trait item to the trait item "descriptor" - pub impl_or_trait_items: RefCell>>, + pub impl_or_trait_items: RefCell>>, /// Maps from a trait def-id to a list of the def-ids of its trait items pub trait_item_def_ids: RefCell>>>, @@ -271,7 +275,7 @@ pub struct ctxt<'tcx> { pub map: ast_map::Map<'tcx>, pub freevars: RefCell, - pub tcache: RefCell>>, + pub tcache: RefCell>>, pub rcache: RefCell>>, pub tc_cache: RefCell, ty::contents::TypeContents>>, pub ast_ty_to_ty_cache: RefCell>>, @@ -483,7 +487,7 @@ impl<'tcx> ctxt<'tcx> { { let interner = RefCell::new(FnvHashMap()); let common_types = CommonTypes::new(&arenas.type_, &interner); - + let dep_graph = Rc::new(DepGraph::new()); tls::enter(ctxt { arenas: arenas, interner: interner, @@ -491,6 +495,7 @@ impl<'tcx> ctxt<'tcx> { bare_fn_interner: RefCell::new(FnvHashMap()), region_interner: RefCell::new(FnvHashMap()), stability_interner: RefCell::new(FnvHashMap()), + dep_graph: dep_graph.clone(), types: common_types, named_region_map: named_region_map, region_maps: region_maps, @@ -508,11 +513,11 @@ impl<'tcx> ctxt<'tcx> { fulfilled_predicates: RefCell::new(traits::FulfilledPredicates::new()), map: map, freevars: RefCell::new(freevars), - tcache: RefCell::new(DefIdMap()), + tcache: RefCell::new(DepTrackingMap::new(dep_graph.clone())), rcache: RefCell::new(FnvHashMap()), tc_cache: RefCell::new(FnvHashMap()), ast_ty_to_ty_cache: RefCell::new(NodeMap()), - impl_or_trait_items: RefCell::new(DefIdMap()), + impl_or_trait_items: RefCell::new(DepTrackingMap::new(dep_graph.clone())), trait_item_def_ids: RefCell::new(DefIdMap()), trait_items_cache: RefCell::new(DefIdMap()), ty_param_defs: RefCell::new(NodeMap()), diff --git a/src/librustc/middle/ty/maps.rs b/src/librustc/middle/ty/maps.rs new file mode 100644 index 0000000000000..778b1e2f70f85 --- /dev/null +++ b/src/librustc/middle/ty/maps.rs @@ -0,0 +1,31 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use dep_graph::{DepNode, DepTrackingMapId}; +use middle::def_id::DefId; +use middle::ty; +use std::marker::PhantomData; + +macro_rules! dep_map_ty { + ($name:ident : ($key:ty) -> $value:ty) => { + pub struct $name<'tcx> { + data: PhantomData<&'tcx ()> + } + + impl<'tcx> DepTrackingMapId for $name<'tcx> { + type Key = $key; + type Value = $value; + fn to_dep_node(key: &$key) -> DepNode { DepNode::$name(*key) } + } + } +} + +dep_map_ty! { ImplOrTraitItems: (DefId) -> ty::ImplOrTraitItem<'tcx> } +dep_map_ty! { Tcache: (DefId) -> ty::TypeScheme<'tcx> } diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index 7477c4dead031..dbdab1d75fa22 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -19,6 +19,7 @@ pub use self::ImplOrTraitItem::*; pub use self::IntVarValue::*; pub use self::LvaluePreference::*; +use dep_graph::{self, DepGraph, DepNode}; use front::map as ast_map; use front::map::LinkedPath; use middle; @@ -31,8 +32,8 @@ use middle::traits; use middle::ty; use middle::ty::fold::TypeFolder; use middle::ty::walk::TypeWalker; -use util::common::memoized; -use util::nodemap::{NodeMap, NodeSet, DefIdMap}; +use util::common::{memoized, MemoizationMap}; +use util::nodemap::{NodeMap, NodeSet}; use util::nodemap::FnvHashMap; use serialize::{Encodable, Encoder, Decodable, Decoder}; @@ -52,6 +53,7 @@ use syntax::parse::token::{InternedString, special_idents}; use rustc_front::hir; use rustc_front::hir::{ItemImpl, ItemTrait}; use rustc_front::hir::{MutImmutable, MutMutable, Visibility}; +use rustc_front::intravisit::Visitor; pub use self::sty::{Binder, DebruijnIndex}; pub use self::sty::{BuiltinBound, BuiltinBounds, ExistentialBounds}; @@ -82,6 +84,7 @@ pub mod error; pub mod fast_reject; pub mod fold; pub mod _match; +pub mod maps; pub mod outlives; pub mod relate; pub mod walk; @@ -1363,6 +1366,10 @@ pub struct TraitDef<'tcx> { } impl<'tcx> TraitDef<'tcx> { + pub fn def_id(&self) -> DefId { + self.trait_ref.def_id + } + // returns None if not yet calculated pub fn object_safety(&self) -> Option { if self.flags.get().intersects(TraitFlags::OBJECT_SAFETY_VALID) { @@ -1925,12 +1932,14 @@ impl LvaluePreference { /// into the map by the `typeck::collect` phase. If the def-id is external, /// then we have to go consult the crate loading code (and cache the result for /// the future). -fn lookup_locally_or_in_crate_store(descr: &str, +fn lookup_locally_or_in_crate_store(descr: &str, def_id: DefId, - map: &RefCell>, - load_external: F) -> V where - V: Clone, - F: FnOnce() -> V, + dep_graph: &DepGraph, + map: &RefCell, + load_external: F) + -> M::Value where + M: MemoizationMap, + F: FnOnce() -> M::Value, { match map.borrow().get(&def_id).cloned() { Some(v) => { return v; } @@ -1941,7 +1950,14 @@ fn lookup_locally_or_in_crate_store(descr: &str, panic!("No def'n found for {:?} in tcx.{}", def_id, descr); } let v = load_external(); - map.borrow_mut().insert(def_id, v.clone()); + + // Don't consider this a write from the current task, since we are + // loading from another crate. (Note that the current task will + // already have registered a read in the call to `get` above.) + dep_graph.with_ignore(|| { + map.borrow_mut().insert(def_id, v.clone()); + }); + v } @@ -2268,13 +2284,13 @@ impl<'tcx> ctxt<'tcx> { pub fn impl_or_trait_item(&self, id: DefId) -> ImplOrTraitItem<'tcx> { lookup_locally_or_in_crate_store( - "impl_or_trait_items", id, &self.impl_or_trait_items, + "impl_or_trait_items", id, &self.dep_graph, &self.impl_or_trait_items, || self.sess.cstore.impl_or_trait_item(self, id)) } pub fn trait_item_def_ids(&self, id: DefId) -> Rc> { lookup_locally_or_in_crate_store( - "trait_item_def_ids", id, &self.trait_item_def_ids, + "trait_item_def_ids", id, &self.dep_graph, &self.trait_item_def_ids, || Rc::new(self.sess.cstore.trait_item_def_ids(id))) } @@ -2282,7 +2298,7 @@ impl<'tcx> ctxt<'tcx> { /// an inherent impl. pub fn impl_trait_ref(&self, id: DefId) -> Option> { lookup_locally_or_in_crate_store( - "impl_trait_refs", id, &self.impl_trait_refs, + "impl_trait_refs", id, &self.dep_graph, &self.impl_trait_refs, || self.sess.cstore.impl_trait_ref(self, id)) } @@ -2343,14 +2359,14 @@ impl<'tcx> ctxt<'tcx> { // the type cache. Returns the type parameters and type. pub fn lookup_item_type(&self, did: DefId) -> TypeScheme<'tcx> { lookup_locally_or_in_crate_store( - "tcache", did, &self.tcache, + "tcache", did, &self.dep_graph, &self.tcache, || self.sess.cstore.item_type(self, did)) } /// Given the did of a trait, returns its canonical trait ref. pub fn lookup_trait_def(&self, did: DefId) -> &'tcx TraitDef<'tcx> { lookup_locally_or_in_crate_store( - "trait_defs", did, &self.trait_defs, + "trait_defs", did, &self.dep_graph, &self.trait_defs, || self.alloc_trait_def(self.sess.cstore.trait_def(self, did)) ) } @@ -2360,7 +2376,7 @@ impl<'tcx> ctxt<'tcx> { /// use lookup_adt_def instead. pub fn lookup_adt_def_master(&self, did: DefId) -> AdtDefMaster<'tcx> { lookup_locally_or_in_crate_store( - "adt_defs", did, &self.adt_defs, + "adt_defs", did, &self.dep_graph, &self.adt_defs, || self.sess.cstore.adt_def(self, did) ) } @@ -2375,14 +2391,14 @@ impl<'tcx> ctxt<'tcx> { /// Given the did of an item, returns its full set of predicates. pub fn lookup_predicates(&self, did: DefId) -> GenericPredicates<'tcx> { lookup_locally_or_in_crate_store( - "predicates", did, &self.predicates, + "predicates", did, &self.dep_graph, &self.predicates, || self.sess.cstore.item_predicates(self, did)) } /// Given the did of a trait, returns its superpredicates. pub fn lookup_super_predicates(&self, did: DefId) -> GenericPredicates<'tcx> { lookup_locally_or_in_crate_store( - "super_predicates", did, &self.super_predicates, + "super_predicates", did, &self.dep_graph, &self.super_predicates, || self.sess.cstore.item_super_predicates(self, did)) } @@ -2426,7 +2442,7 @@ impl<'tcx> ctxt<'tcx> { pub fn item_variances(&self, item_id: DefId) -> Rc { lookup_locally_or_in_crate_store( - "item_variance_map", item_id, &self.item_variance_map, + "item_variance_map", item_id, &self.dep_graph, &self.item_variance_map, || Rc::new(self.sess.cstore.item_variances(item_id))) } @@ -2727,6 +2743,15 @@ impl<'tcx> ctxt<'tcx> { pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option { Some(self.tables.borrow().upvar_capture_map.get(&upvar_id).unwrap().clone()) } + + + pub fn visit_all_items_in_krate(&self, + dep_node_fn: F, + visitor: &mut V) + where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx> + { + dep_graph::visit_all_items_in_krate(self, dep_node_fn, visitor); + } } /// The category of explicit self. diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 6fb8c03370142..2d1a933b1a0f7 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -125,6 +125,7 @@ pub struct Options { pub parse_only: bool, pub no_trans: bool, pub treat_err_as_bug: bool, + pub dump_dep_graph: bool, pub no_analysis: bool, pub debugging_opts: DebuggingOptions, pub prints: Vec, @@ -234,6 +235,7 @@ pub fn basic_options() -> Options { parse_only: false, no_trans: false, treat_err_as_bug: false, + dump_dep_graph: false, no_analysis: false, debugging_opts: basic_debugging_options(), prints: Vec::new(), @@ -604,6 +606,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "run all passes except translation; no output"), treat_err_as_bug: bool = (false, parse_bool, "treat all errors that occur as bugs"), + dump_dep_graph: bool = (false, parse_bool, + "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv)"), no_analysis: bool = (false, parse_bool, "parse and expand the source, but run no analysis"), extra_plugins: Vec = (Vec::new(), parse_list, @@ -925,6 +929,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { let parse_only = debugging_opts.parse_only; let no_trans = debugging_opts.no_trans; let treat_err_as_bug = debugging_opts.treat_err_as_bug; + let dump_dep_graph = debugging_opts.dump_dep_graph; let no_analysis = debugging_opts.no_analysis; if debugging_opts.debug_llvm { @@ -1099,6 +1104,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { parse_only: parse_only, no_trans: no_trans, treat_err_as_bug: treat_err_as_bug, + dump_dep_graph: dump_dep_graph, no_analysis: no_analysis, debugging_opts: debugging_opts, prints: prints, diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs index 2a3d9cfa6b888..413d7df2a942d 100644 --- a/src/librustc/util/common.rs +++ b/src/librustc/util/common.rs @@ -228,11 +228,9 @@ pub fn block_query

(b: &hir::Block, p: P) -> bool where P: FnMut(&hir::Expr) - /// } /// ``` #[inline(always)] -pub fn memoized(cache: &RefCell>, arg: T, f: F) -> U - where T: Clone + Hash + Eq, - U: Clone, - S: HashState, - F: FnOnce(T) -> U, +pub fn memoized(cache: &RefCell, arg: M::Key, f: F) -> M::Value + where M: MemoizationMap, + F: FnOnce(M::Key) -> M::Value, { let key = arg.clone(); let result = cache.borrow().get(&key).cloned(); @@ -246,6 +244,26 @@ pub fn memoized(cache: &RefCell>, arg: T, f: F) -> } } +pub trait MemoizationMap { + type Key: Clone; + type Value: Clone; + fn get(&self, key: &Self::Key) -> Option<&Self::Value>; + fn insert(&mut self, key: Self::Key, value: Self::Value) -> Option; +} + +impl MemoizationMap for HashMap + where K: Hash+Eq+Clone, V: Clone, S: HashState +{ + type Key = K; + type Value = V; + fn get(&self, key: &K) -> Option<&V> { + self.get(key) + } + fn insert(&mut self, key: K, value: V) -> Option { + self.insert(key, value) + } +} + #[cfg(unix)] pub fn path2cstr(p: &Path) -> CString { use std::os::unix::prelude::*; diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 64bbf49e3a8e0..42b1edf556ec2 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -20,6 +20,7 @@ pub use self::MovedValueUseKind::*; use self::InteriorKind::*; +use rustc::dep_graph::DepNode; use rustc::front::map as hir_map; use rustc::front::map::blocks::FnParts; use rustc::middle::cfg; @@ -108,7 +109,7 @@ pub fn check_crate(tcx: &ty::ctxt) { } }; - tcx.map.krate().visit_all_items(&mut bccx); + tcx.visit_all_items_in_krate(DepNode::BorrowCheck, &mut bccx); if tcx.sess.borrowck_stats() { println!("--- borrowck stats ---"); diff --git a/src/librustc_data_structures/dependency/mod.rs b/src/librustc_data_structures/dependency/mod.rs new file mode 100644 index 0000000000000..61cb863846290 --- /dev/null +++ b/src/librustc_data_structures/dependency/mod.rs @@ -0,0 +1,121 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Dependency graph used for incremental compilation. + +use std::cell::RefCell; +use std::fmt::Debug; +use std::hash::Hash; +use self::state::DepGraphState; + +mod state; + +#[cfg(test)] +mod test; + +pub struct DepGraph { + state: RefCell> +} + +pub trait DepNodeId: Clone + Debug + Hash + PartialEq + Eq { +} + +impl DepGraph { + pub fn new() -> DepGraph { + DepGraph { + state: RefCell::new(DepGraphState::new()) + } + } + + pub fn dependents(&self, node: ID) -> Vec { + self.state.borrow().dependents(node) + } + + pub fn in_ignore<'graph>(&'graph self) -> IgnoreTask<'graph, ID> { + IgnoreTask::new(self) + } + + pub fn with_ignore(&self, op: OP) -> R + where OP: FnOnce() -> R + { + let _task = self.in_ignore(); + op() + } + + pub fn in_task<'graph>(&'graph self, key: ID) -> DepTask<'graph, ID> { + DepTask::new(self, key) + } + + pub fn with_task(&self, key: ID, op: OP) -> R + where OP: FnOnce() -> R + { + let _task = self.in_task(key); + op() + } + + /// Indicates that the current task `C` reads `v` by adding an + /// edge from `v` to `C`. If there is no current task, panics. If + /// you want to suppress this edge, use `ignore`. + pub fn read(&self, v: ID) { + self.state.borrow_mut().read(v); + } + + /// Indicates that the current task `C` writes `v` by adding an + /// edge from `C` to `v`. If there is no current task, panics. If + /// you want to suppress this edge, use `ignore`. + pub fn write(&self, v: ID) { + self.state.borrow_mut().write(v); + } + + // Low-level graph introspection, mainly intended for dumping out state. + + pub fn nodes(&self) -> Vec { + self.state.borrow().nodes() + } + + pub fn edges(&self) -> Vec<(ID,ID)> { + self.state.borrow().edges() + } +} + +pub struct DepTask<'graph, ID: 'graph + DepNodeId> { + graph: &'graph DepGraph, + key: ID, +} + +impl<'graph, ID: DepNodeId> DepTask<'graph, ID> { + pub fn new(graph: &'graph DepGraph, key: ID) -> DepTask<'graph, ID> { + graph.state.borrow_mut().push_task(key.clone()); + DepTask { graph: graph, key: key } + } +} + +impl<'graph, ID: DepNodeId> Drop for DepTask<'graph, ID> { + fn drop(&mut self) { + self.graph.state.borrow_mut().pop_task(self.key.clone()); + } +} + +pub struct IgnoreTask<'graph, ID: 'graph + DepNodeId> { + graph: &'graph DepGraph, +} + +impl<'graph, ID: DepNodeId> IgnoreTask<'graph, ID> { + pub fn new(graph: &'graph DepGraph) -> IgnoreTask<'graph, ID> { + graph.state.borrow_mut().push_ignore(); + IgnoreTask { graph: graph } + } +} + +impl<'graph, ID: DepNodeId> Drop for IgnoreTask<'graph, ID> { + fn drop(&mut self) { + self.graph.state.borrow_mut().pop_ignore(); + } +} diff --git a/src/librustc_data_structures/dependency/state.rs b/src/librustc_data_structures/dependency/state.rs new file mode 100644 index 0000000000000..b088af39d51b4 --- /dev/null +++ b/src/librustc_data_structures/dependency/state.rs @@ -0,0 +1,157 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fnv::{FnvHashSet, FnvHashMap}; +use graph::{Graph, NodeIndex}; + +use super::DepNodeId; + +pub struct DepGraphState { + graph: Graph, + nodes: FnvHashMap, + edges: FnvHashSet<(NodeIndex, NodeIndex)>, + open_nodes: Vec, +} + +#[derive(Clone, Debug, PartialEq)] +enum OpenNode { + Node(NodeIndex), + Ignore, +} + +impl DepGraphState { + pub fn new() -> DepGraphState { + DepGraphState { + graph: Graph::new(), + nodes: FnvHashMap(), + edges: FnvHashSet(), + open_nodes: Vec::new() + } + } + + /// Creates an entry for `node` in the graph. + fn make_node(&mut self, node: ID) -> NodeIndex { + if let Some(&i) = self.nodes.get(&node) { + return i; + } + + let index = self.graph.add_node(node.clone()); + self.nodes.insert(node, index); + index + } + + /// Top of the stack of open nodes. + fn current_node(&self) -> Option { + self.open_nodes.last().cloned() + } + + /// All nodes reachable from `node`. In other words, things that + /// will have to be recomputed if `node` changes. + pub fn dependents(&self, node: ID) -> Vec { + match self.nodes.get(&node) { + None => vec![], + Some(&index) => + self.graph.depth_traverse(index) + .map(|dependent_node| self.graph.node_data(dependent_node).clone()) + .collect() + } + } + + pub fn push_ignore(&mut self) { + self.open_nodes.push(OpenNode::Ignore); + } + + pub fn pop_ignore(&mut self) { + let popped_node = self.open_nodes.pop().unwrap(); + assert_eq!(popped_node, OpenNode::Ignore); + } + + pub fn push_task(&mut self, key: ID) { + let top_node = self.current_node(); + + let new_node = self.make_node(key.clone()); + self.open_nodes.push(OpenNode::Node(new_node)); + + // if we are in the midst of doing task T, then this new task + // N is a subtask of T, so add an edge N -> T. + if let Some(top_node) = top_node { + self.add_edge_from_open_node(top_node, |t| (new_node, t)); + } + } + + pub fn pop_task(&mut self, key: ID) { + let popped_node = self.open_nodes.pop().unwrap(); + assert_eq!(OpenNode::Node(self.nodes[&key]), popped_node); + } + + /// Indicates that the current task `C` reads `v` by adding an + /// edge from `v` to `C`. If there is no current task, panics. If + /// you want to suppress this edge, use `ignore`. + pub fn read(&mut self, v: ID) { + let source = self.make_node(v); + self.add_edge_from_current_node(|current| (source, current)) + } + + /// Indicates that the current task `C` writes `v` by adding an + /// edge from `C` to `v`. If there is no current task, panics. If + /// you want to suppress this edge, use `ignore`. + pub fn write(&mut self, v: ID) { + let target = self.make_node(v); + self.add_edge_from_current_node(|current| (current, target)) + } + + /// Invoke `add_edge_from_open_node` with the top of the stack, or + /// panic if stack is empty. + fn add_edge_from_current_node(&mut self, + op: OP) + where OP: FnOnce(NodeIndex) -> (NodeIndex, NodeIndex) + { + match self.current_node() { + Some(open_node) => self.add_edge_from_open_node(open_node, op), + None => panic!("no current node, cannot add edge into dependency graph") + } + } + + /// Adds an edge to or from the `open_node`, assuming `open_node` + /// is not `Ignore`. The direction of the edge is determined by + /// the closure `op` --- we pass as argument the open node `n`, + /// and the closure returns a (source, target) tuple, which should + /// include `n` in one spot or another. + fn add_edge_from_open_node(&mut self, + open_node: OpenNode, + op: OP) + where OP: FnOnce(NodeIndex) -> (NodeIndex, NodeIndex) + { + let (source, target) = match open_node { + OpenNode::Node(n) => op(n), + OpenNode::Ignore => { return; } + }; + + if self.edges.insert((source, target)) { + debug!("adding edge from {:?} to {:?}", + self.graph.node_data(source), + self.graph.node_data(target)); + self.graph.add_edge(source, target, ()); + } + } + + pub fn nodes(&self) -> Vec { + self.nodes.keys().cloned().collect() + } + + pub fn edges(&self) -> Vec<(ID,ID)> { + self.graph.all_edges() + .iter() + .map(|edge| (edge.source(), edge.target())) + .map(|(source, target)| (self.graph.node_data(source).clone(), + self.graph.node_data(target).clone())) + .collect() + } +} diff --git a/src/librustc_data_structures/dependency/test.rs b/src/librustc_data_structures/dependency/test.rs new file mode 100644 index 0000000000000..f1275398f9dd1 --- /dev/null +++ b/src/librustc_data_structures/dependency/test.rs @@ -0,0 +1,35 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::{DepGraph, DepNodeId}; + +impl DepNodeId for &'static str { } + +#[test] +fn foo() { + let graph = DepGraph::new(); + + graph.with_task("a", || { + graph.read("b"); + graph.write("c"); + }); + + let mut deps = graph.dependents("a"); + deps.sort(); + assert_eq!(deps, vec!["a", "c"]); + + let mut deps = graph.dependents("b"); + deps.sort(); + assert_eq!(deps, vec!["a", "b", "c"]); + + let mut deps = graph.dependents("c"); + deps.sort(); + assert_eq!(deps, vec!["c"]); +} diff --git a/src/librustc_data_structures/graph/mod.rs b/src/librustc_data_structures/graph/mod.rs index 4a3810c822b4d..da6012cfd2fcb 100644 --- a/src/librustc_data_structures/graph/mod.rs +++ b/src/librustc_data_structures/graph/mod.rs @@ -77,10 +77,10 @@ impl Debug for Edge { } } -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub struct NodeIndex(pub usize); -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub struct EdgeIndex(pub usize); pub const INVALID_EDGE_INDEX: EdgeIndex = EdgeIndex(usize::MAX); diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 0ea7cfa3902fa..f78b047864c00 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -36,6 +36,7 @@ extern crate serialize as rustc_serialize; // used by deriving pub mod bitvec; +pub mod dependency; pub mod graph; pub mod ivar; pub mod snapshot_vec; diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 69e065b3e8f97..b200909f2705c 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -791,7 +791,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, time(time_passes, "rvalue checking", - || middle::check_rvalues::check_crate(tcx, krate)); + || middle::check_rvalues::check_crate(tcx)); // Avoid overwhelming user with errors if type checking failed. // I'm not sure how helpful this is, to be honest, but it avoids diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 47b89e3be5d43..ba5ecc22e7474 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -204,6 +204,7 @@ impl PpSourceMode { let annotation = TypedAnnotation { tcx: tcx, }; + let _ignore = tcx.dep_graph.in_ignore(); f(&annotation, payload, &ast_map.forest.krate) diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 39a315f3c41c7..ad3c173676148 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -23,6 +23,7 @@ extern crate rustc_front; use build; use dot; use transform::*; +use rustc::dep_graph::DepNode; use rustc::mir::repr::Mir; use hair::cx::Cx; use std::fs::File; @@ -47,7 +48,7 @@ pub fn build_mir_for_crate<'tcx>(tcx: &ty::ctxt<'tcx>) -> MirMap<'tcx> { tcx: tcx, map: &mut map, }; - tcx.map.krate().visit_all_items(&mut dump); + tcx.visit_all_items_in_krate(DepNode::MirMapConstruction, &mut dump); } map } diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 9f70598198eb9..3c5f05217d49a 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -38,6 +38,7 @@ use std::mem::replace; use rustc_front::hir; use rustc_front::intravisit::{self, Visitor}; +use rustc::dep_graph::DepNode; use rustc::middle::def; use rustc::middle::def_id::DefId; use rustc::middle::privacy::{AccessLevel, AccessLevels}; @@ -1443,6 +1444,8 @@ pub fn check_crate(tcx: &ty::ctxt, export_map: &def::ExportMap, external_exports: ExternalExports) -> AccessLevels { + let _task = tcx.dep_graph.in_task(DepNode::Privacy); + let krate = tcx.map.krate(); // Sanity check to make sure that all privacy usage and controls are diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 975323375f18e..48bd771b84144 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -182,8 +182,10 @@ pub fn find_crate_name(sess: Option<&Session>, "rust_out".to_string() } -pub fn build_link_meta(sess: &Session, krate: &hir::Crate, - name: &str) -> LinkMeta { +pub fn build_link_meta(sess: &Session, + krate: &hir::Crate, + name: &str) + -> LinkMeta { let r = LinkMeta { crate_name: name.to_owned(), crate_hash: Svh::calculate(&sess.opts.cg.metadata, krate), diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 83d0106fd4cb3..4c90bed171dec 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -30,6 +30,7 @@ #![feature(const_fn)] #![feature(custom_attribute)] #![allow(unused_attributes)] +#![feature(into_cow)] #![feature(iter_arith)] #![feature(libc)] #![feature(path_relative_from)] diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index cc5322d7f9f46..959925b8a1a21 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -716,6 +716,8 @@ pub fn process_crate<'l, 'tcx>(tcx: &'l ty::ctxt<'tcx>, analysis: &ty::CrateAnalysis, cratename: &str, odir: Option<&Path>) { + let _ignore = tcx.dep_graph.in_ignore(); + if generated_code(krate.span) { return; } diff --git a/src/librustc_trans/trans/assert_dep_graph.rs b/src/librustc_trans/trans/assert_dep_graph.rs new file mode 100644 index 0000000000000..7b2988f86bfde --- /dev/null +++ b/src/librustc_trans/trans/assert_dep_graph.rs @@ -0,0 +1,218 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This pass is only used for the UNIT TESTS and DEBUGGING NEEDS +//! around dependency graph construction. It serves two purposes; it +//! will dump graphs in graphviz form to disk, and it searches for +//! `#[rustc_if_this_changed]` and `#[rustc_then_this_would_need]` +//! annotations. These annotations can be used to test whether paths +//! exist in the graph. We report errors on each +//! `rustc_if_this_changed` annotation. If a path exists in all +//! cases, then we would report "all path(s) exist". Otherwise, we +//! report: "no path to `foo`" for each case where no path exists. +//! `compile-fail` tests can then be used to check when paths exist or +//! do not. +//! +//! The full form of the `rustc_if_this_changed` annotation is +//! `#[rustc_if_this_changed(id)]`. The `"id"` is optional and +//! defaults to `"id"` if omitted. +//! +//! Example: +//! +//! ``` +//! #[rustc_if_this_changed] +//! fn foo() { } +//! +//! #[rustc_then_this_would_need("trans")] //~ ERROR no path from `foo` +//! fn bar() { } +//! ``` + +use graphviz as dot; +use rustc::dep_graph::{DepGraph, DepNode}; +use rustc::middle::ty; +use rustc_data_structures::fnv::{FnvHashMap, FnvHashSet}; +use rustc_front::hir; +use rustc_front::intravisit::Visitor; +use std::borrow::IntoCow; +use std::env; +use std::fs::File; +use std::io::Write; +use std::rc::Rc; +use syntax::ast; +use syntax::attr::AttrMetaMethods; +use syntax::codemap::Span; +use syntax::parse::token::InternedString; + +const IF_THIS_CHANGED: &'static str = "rustc_if_this_changed"; +const THEN_THIS_WOULD_NEED: &'static str = "rustc_then_this_would_need"; +const ID: &'static str = "id"; + +pub fn assert_dep_graph(tcx: &ty::ctxt) { + let _ignore = tcx.dep_graph.in_ignore(); + + if tcx.sess.opts.dump_dep_graph { + dump_graph(tcx); + } + + // Find annotations supplied by user (if any). + let (if_this_changed, then_this_would_need) = { + let mut visitor = IfThisChanged { tcx: tcx, + if_this_changed: FnvHashMap(), + then_this_would_need: FnvHashMap() }; + tcx.map.krate().visit_all_items(&mut visitor); + (visitor.if_this_changed, visitor.then_this_would_need) + }; + + // Check paths. + check_paths(tcx, &if_this_changed, &then_this_would_need); +} + +type SourceHashMap = FnvHashMap>; +type TargetHashMap = FnvHashMap>; + +struct IfThisChanged<'a, 'tcx:'a> { + tcx: &'a ty::ctxt<'tcx>, + if_this_changed: SourceHashMap, + then_this_would_need: TargetHashMap, +} + +impl<'a, 'tcx> Visitor<'tcx> for IfThisChanged<'a, 'tcx> { + fn visit_item(&mut self, item: &'tcx hir::Item) { + let def_id = self.tcx.map.local_def_id(item.id); + for attr in self.tcx.get_attrs(def_id).iter() { + if attr.check_name(IF_THIS_CHANGED) { + let mut id = None; + for meta_item in attr.meta_item_list().unwrap_or_default() { + match meta_item.node { + ast::MetaWord(ref s) if id.is_none() => id = Some(s.clone()), + _ => { + self.tcx.sess.span_err( + meta_item.span, + &format!("unexpected meta-item {:?}", meta_item.node)); + } + } + } + let id = id.unwrap_or(InternedString::new(ID)); + self.if_this_changed.entry(id) + .or_insert(FnvHashSet()) + .insert((attr.span, DepNode::Hir(def_id))); + } else if attr.check_name(THEN_THIS_WOULD_NEED) { + let mut dep_node_interned = None; + let mut id = None; + for meta_item in attr.meta_item_list().unwrap_or_default() { + match meta_item.node { + ast::MetaWord(ref s) if dep_node_interned.is_none() => + dep_node_interned = Some(s.clone()), + ast::MetaWord(ref s) if id.is_none() => + id = Some(s.clone()), + _ => { + self.tcx.sess.span_err( + meta_item.span, + &format!("unexpected meta-item {:?}", meta_item.node)); + } + } + } + let dep_node_str = dep_node_interned.as_ref().map(|s| &**s); + let dep_node = match dep_node_str { + Some("BorrowCheck") => DepNode::BorrowCheck(def_id), + Some("TransCrateItem") => DepNode::TransCrateItem(def_id), + Some("TypeckItemType") => DepNode::TypeckItemType(def_id), + Some("TypeckItemBody") => DepNode::TypeckItemBody(def_id), + _ => { + self.tcx.sess.span_fatal( + attr.span, + &format!("unrecognized pass {:?}", dep_node_str)); + } + }; + let id = id.unwrap_or(InternedString::new(ID)); + self.then_this_would_need + .entry(id) + .or_insert(FnvHashSet()) + .insert((attr.span, dep_node_interned.clone().unwrap(), item.id, dep_node)); + } + } + } +} + +fn check_paths(tcx: &ty::ctxt, + if_this_changed: &SourceHashMap, + then_this_would_need: &TargetHashMap) +{ + for (id, sources) in if_this_changed { + let targets = match then_this_would_need.get(id) { + Some(targets) => targets, + None => { + for &(source_span, _) in sources.iter().take(1) { + tcx.sess.span_err( + source_span, + &format!("no targets for id `{}`", id)); + } + continue; + } + }; + + for &(source_span, ref source_dep_node) in sources { + let dependents = tcx.dep_graph.dependents(source_dep_node.clone()); + for &(_, ref target_pass, target_node_id, ref target_dep_node) in targets { + if !dependents.contains(&target_dep_node) { + let target_def_id = tcx.map.local_def_id(target_node_id); + tcx.sess.span_err( + source_span, + &format!("no path to {} for `{}`", + target_pass, + tcx.item_path_str(target_def_id))); + } + } + } + } +} + +fn dump_graph(tcx: &ty::ctxt) { + let path: String = env::var("RUST_DEP_GRAPH").unwrap_or_else(|_| format!("/tmp/dep_graph.dot")); + let mut v = Vec::new(); + dot::render(&GraphvizDepGraph(tcx.dep_graph.clone()), &mut v).unwrap(); + File::create(&path).and_then(|mut f| f.write_all(&v)).unwrap(); +} + +pub struct GraphvizDepGraph(Rc); + +impl<'a, 'tcx> dot::GraphWalk<'a, DepNode, (DepNode, DepNode)> for GraphvizDepGraph { + fn nodes(&self) -> dot::Nodes { + self.0.nodes().into_cow() + } + fn edges(&self) -> dot::Edges<(DepNode, DepNode)> { + self.0.edges().into_cow() + } + fn source(&self, edge: &(DepNode, DepNode)) -> DepNode { + edge.0.clone() + } + fn target(&self, edge: &(DepNode, DepNode)) -> DepNode { + edge.1.clone() + } +} + +impl<'a, 'tcx> dot::Labeller<'a, DepNode, (DepNode, DepNode)> for GraphvizDepGraph { + fn graph_id(&self) -> dot::Id { + dot::Id::new("DependencyGraph").unwrap() + } + fn node_id(&self, n: &DepNode) -> dot::Id { + let s: String = + format!("{:?}", n).chars() + .map(|c| if c == '_' || c.is_alphanumeric() { c } else { '_' }) + .collect(); + debug!("n={:?} s={:?}", n, s); + dot::Id::new(s).unwrap() + } + fn node_label(&self, n: &DepNode) -> dot::LabelText { + dot::LabelText::label(format!("{:?}", n)) + } +} diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 838a5435d4fee..d618e1656438f 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -43,12 +43,14 @@ use middle::weak_lang_items; use middle::pat_util::simple_name; use middle::subst::Substs; use middle::ty::{self, Ty, HasTypeFlags}; +use rustc::dep_graph::DepNode; use rustc::front::map as hir_map; use rustc_mir::mir_map::MirMap; use session::config::{self, NoDebugInfo, FullDebugInfo}; use session::Session; use trans::_match; use trans::adt; +use trans::assert_dep_graph; use trans::attributes; use trans::build::*; use trans::builder::{Builder, noname}; @@ -2974,7 +2976,6 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>, analysis: ty::CrateAnalysis) -> CrateTranslation { let ty::CrateAnalysis { export_map, reachable, name, .. } = analysis; - let krate = tcx.map.krate(); let check_overflow = if let Some(v) = tcx.sess.opts.debugging_opts.force_overflow_checks { v @@ -3008,7 +3009,12 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>, } } - let link_meta = link::build_link_meta(&tcx.sess, krate, name); + let (link_meta, no_builtins) = tcx.dep_graph.with_task(DepNode::TransLinkMeta, || { + let krate = tcx.map.krate(); + let no_builtins = attr::contains_name(&krate.attrs, "no_builtins"); + let link_meta = link::build_link_meta(&tcx.sess, krate, name); + (link_meta, no_builtins) + }); let codegen_units = tcx.sess.opts.cg.codegen_units; let shared_ccx = SharedCrateContext::new(&link_meta.crate_name, @@ -3032,6 +3038,7 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>, // details on why we walk in this particular way. { let _icx = push_ctxt("text"); + let krate = tcx.dep_graph.with_ignore(|| tcx.map.krate()); intravisit::walk_mod(&mut TransItemsWithinModVisitor { ccx: &ccx }, &krate.module); krate.visit_all_items(&mut TransModVisitor { ccx: &ccx }); } @@ -3053,7 +3060,10 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>, let reachable_symbol_ids = filter_reachable_ids(&shared_ccx); // Translate the metadata. - let metadata = write_metadata(&shared_ccx, krate, &reachable_symbol_ids, mir_map); + let metadata = tcx.dep_graph.with_task(DepNode::TransWriteMetadata, || { + let krate = tcx.map.krate(); + write_metadata(&shared_ccx, krate, &reachable_symbol_ids, mir_map) + }); if shared_ccx.sess().trans_stats() { let stats = shared_ccx.stats(); @@ -3125,7 +3135,8 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>, llcx: shared_ccx.metadata_llcx(), llmod: shared_ccx.metadata_llmod(), }; - let no_builtins = attr::contains_name(&krate.attrs, "no_builtins"); + + assert_dep_graph::assert_dep_graph(tcx); CrateTranslation { modules: modules, @@ -3146,6 +3157,10 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>, /// ensures that the immediate contents of each module is processed /// entirely before we proceed to find more modules, helping to ensure /// an equitable distribution amongst codegen-units. +/// +/// When it comes to dependency tracking, TransModVisitor runs with +/// nothing on the dep stack. We push the task onto the dep stack in +/// `TransItemsWithinModVisitor`. pub struct TransModVisitor<'a, 'tcx: 'a> { pub ccx: &'a CrateContext<'a, 'tcx>, } @@ -3179,7 +3194,12 @@ impl<'a, 'tcx, 'v> Visitor<'v> for TransItemsWithinModVisitor<'a, 'tcx> { // skip modules, they will be uncovered by the TransModVisitor } _ => { - trans_item(self.ccx, i); + // we are responsible for managing dependencies here, + // so push the current task + let def_id = self.ccx.tcx().map.local_def_id(i.id); + self.ccx.tcx().dep_graph.with_task(DepNode::TransCrateItem(def_id), || { + trans_item(self.ccx, i); + }); intravisit::walk_item(self, i); } } diff --git a/src/librustc_trans/trans/inline.rs b/src/librustc_trans/trans/inline.rs index 29965755eac76..89399043c964e 100644 --- a/src/librustc_trans/trans/inline.rs +++ b/src/librustc_trans/trans/inline.rs @@ -15,12 +15,13 @@ use middle::subst::Substs; use trans::base::{push_ctxt, trans_item, get_item_val, trans_fn}; use trans::common::*; +use rustc::dep_graph::DepNode; use rustc_front::hir; -fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) - -> Option { +fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) -> Option { debug!("instantiate_inline({:?})", fn_id); let _icx = push_ctxt("instantiate_inline"); + let _task = ccx.tcx().dep_graph.in_task(DepNode::TransInlinedItem(fn_id)); match ccx.external().borrow().get(&fn_id) { Some(&Some(node_id)) => { diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index 27f5e31eaaf7c..5e70e0f67512c 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -39,6 +39,7 @@ use trans::machine::llsize_of; use trans::type_::Type; use middle::ty::{self, Ty, HasTypeFlags}; use middle::subst::Substs; +use rustc::dep_graph::DepNode; use rustc_front::hir; use syntax::abi::{self, RustIntrinsic}; use syntax::ast; @@ -103,6 +104,7 @@ pub fn span_transmute_size_error(a: &Session, b: Span, msg: &str) { /// Performs late verification that intrinsics are used correctly. At present, /// the only intrinsic that needs such verification is `transmute`. pub fn check_intrinsics(ccx: &CrateContext) { + let _task = ccx.tcx().dep_graph.in_task(DepNode::IntrinsicUseCheck); let mut last_failing_id = None; for transmute_restriction in ccx.tcx().transmute_restrictions.borrow().iter() { // Sometimes, a single call to transmute will push multiple diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs index b102e96af20e2..1fbc0d5c01529 100644 --- a/src/librustc_trans/trans/mod.rs +++ b/src/librustc_trans/trans/mod.rs @@ -20,6 +20,7 @@ mod macros; mod adt; mod asm; +mod assert_dep_graph; mod attributes; mod base; mod basic_block; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index f348d5411c347..f9654957870bd 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -82,6 +82,7 @@ use self::TupleArgumentsFlag::*; use astconv::{self, ast_region_to_region, ast_ty_to_ty, AstConv, PathParamMode}; use check::_match::pat_ctxt; +use dep_graph::DepNode; use fmt_macros::{Parser, Piece, Position}; use middle::astconv_util::prohibit_type_params; use middle::cstore::LOCAL_CRATE; @@ -385,6 +386,8 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckItemBodiesVisitor<'a, 'tcx> { } pub fn check_wf_old(ccx: &CrateCtxt) { + let _ignore = ccx.tcx.dep_graph.in_ignore(); // ignore since this code is going away + // FIXME(#25759). The new code below is much more reliable but (for now) // only generates warnings. So as to ensure that we continue // getting errors where we used to get errors, we run the old wf @@ -400,9 +403,8 @@ pub fn check_wf_old(ccx: &CrateCtxt) { } pub fn check_wf_new(ccx: &CrateCtxt) { - let krate = ccx.tcx.map.krate(); let mut visit = wfcheck::CheckTypeWellFormedVisitor::new(ccx); - krate.visit_all_items(&mut visit); + ccx.tcx.visit_all_items_in_krate(DepNode::WfCheck, &mut visit); // If types are not well-formed, it leads to all manner of errors // downstream, so stop reporting errors at this point. @@ -410,16 +412,14 @@ pub fn check_wf_new(ccx: &CrateCtxt) { } pub fn check_item_types(ccx: &CrateCtxt) { - let krate = ccx.tcx.map.krate(); let mut visit = CheckItemTypesVisitor { ccx: ccx }; - krate.visit_all_items(&mut visit); + ccx.tcx.visit_all_items_in_krate(DepNode::TypeckItemType, &mut visit); ccx.tcx.sess.abort_if_errors(); } pub fn check_item_bodies(ccx: &CrateCtxt) { - let krate = ccx.tcx.map.krate(); let mut visit = CheckItemBodiesVisitor { ccx: ccx }; - krate.visit_all_items(&mut visit); + ccx.tcx.visit_all_items_in_krate(DepNode::TypeckItemBody, &mut visit); ccx.tcx.sess.abort_if_errors(); } @@ -429,6 +429,7 @@ pub fn check_drop_impls(ccx: &CrateCtxt) { Some(id) => ccx.tcx.lookup_trait_def(id), None => { return } }; drop_trait.for_each_impl(ccx.tcx, |drop_impl_did| { + let _task = ccx.tcx.dep_graph.in_task(DepNode::Dropck(drop_impl_did)); if drop_impl_did.is_local() { match dropck::check_drop_impl(ccx.tcx, drop_impl_did) { Ok(()) => {} diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index cdbfda40813b4..acfaf0891904c 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -39,6 +39,7 @@ use std::rc::Rc; use syntax::codemap::Span; use syntax::parse::token; use util::nodemap::{DefIdMap, FnvHashMap}; +use rustc::dep_graph::DepNode; use rustc::front::map as hir_map; use rustc::front::map::NodeItem; use rustc_front::intravisit; @@ -509,9 +510,11 @@ fn enforce_trait_manually_implementable(tcx: &ty::ctxt, sp: Span, trait_def_id: } pub fn check_coherence(crate_context: &CrateCtxt) { + let _task = crate_context.tcx.dep_graph.in_task(DepNode::Coherence); + let infcx = new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None, true); CoherenceChecker { crate_context: crate_context, - inference_context: new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None, true), + inference_context: infcx, inherent_impls: RefCell::new(FnvHashMap()), }.check(crate_context.tcx.map.krate()); unsafety::check(crate_context.tcx); diff --git a/src/librustc_typeck/coherence/orphan.rs b/src/librustc_typeck/coherence/orphan.rs index e6e31ba0819c5..34b8f64cf8c2a 100644 --- a/src/librustc_typeck/coherence/orphan.rs +++ b/src/librustc_typeck/coherence/orphan.rs @@ -17,13 +17,14 @@ use middle::traits; use middle::ty; use syntax::ast; use syntax::codemap::Span; +use rustc::dep_graph::DepNode; use rustc_front::intravisit; use rustc_front::hir; use rustc_front::hir::{Item, ItemImpl}; pub fn check(tcx: &ty::ctxt) { let mut orphan = OrphanChecker { tcx: tcx }; - tcx.map.krate().visit_all_items(&mut orphan); + tcx.visit_all_items_in_krate(DepNode::CoherenceOrphanCheck, &mut orphan); } struct OrphanChecker<'cx, 'tcx:'cx> { @@ -232,10 +233,10 @@ impl<'cx, 'tcx> OrphanChecker<'cx, 'tcx> { } Err(traits::OrphanCheckErr::UncoveredTy(param_ty)) => { span_err!(self.tcx.sess, item.span, E0210, - "type parameter `{}` must be used as the type parameter for \ - some local type (e.g. `MyStruct`); only traits defined in \ - the current crate can be implemented for a type parameter", - param_ty); + "type parameter `{}` must be used as the type parameter for \ + some local type (e.g. `MyStruct`); only traits defined in \ + the current crate can be implemented for a type parameter", + param_ty); return; } } diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs index 693c8716ab58a..09e09cf52fe92 100644 --- a/src/librustc_typeck/coherence/overlap.rs +++ b/src/librustc_typeck/coherence/overlap.rs @@ -18,6 +18,7 @@ use middle::ty; use middle::infer::{self, new_infer_ctxt}; use syntax::ast; use syntax::codemap::Span; +use rustc::dep_graph::DepNode; use rustc_front::hir; use rustc_front::intravisit; use util::nodemap::DefIdMap; @@ -28,7 +29,7 @@ pub fn check(tcx: &ty::ctxt) { // this secondary walk specifically checks for some other cases, // like defaulted traits, for which additional overlap rules exist - tcx.map.krate().visit_all_items(&mut overlap); + tcx.visit_all_items_in_krate(DepNode::CoherenceOverlapCheckSpecial, &mut overlap); } struct OverlapChecker<'cx, 'tcx:'cx> { @@ -50,7 +51,10 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> { let trait_defs: Vec<_> = self.tcx.trait_defs.borrow().values().cloned().collect(); for trait_def in trait_defs { - self.tcx.populate_implementations_for_trait_if_necessary(trait_def.trait_ref.def_id); + let _task = + self.tcx.dep_graph.in_task(DepNode::CoherenceOverlapCheck(trait_def.def_id())); + self.tcx.populate_implementations_for_trait_if_necessary( + trait_def.trait_ref.def_id); self.check_for_overlapping_impls_of_trait(trait_def); } } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 86a56e718b5cf..6679403d92f16 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -34,13 +34,12 @@ lazilly and on demand, and include logic that checks for cycles. Demand is driven by calls to `AstConv::get_item_type_scheme` or `AstConv::lookup_trait_def`. -Currently, we "convert" types and traits in three phases (note that +Currently, we "convert" types and traits in two phases (note that conversion only affects the types of items / enum variants / methods; it does not e.g. compute the types of individual expressions): 0. Intrinsics -1. Trait definitions -2. Type definitions +1. Trait/Type definitions Conversion itself is done by simply walking each of the items in turn and invoking an appropriate function (e.g., `trait_def_of_item` or @@ -56,11 +55,6 @@ There are some shortcomings in this design: - Because the type scheme includes defaults, cycles through type parameter defaults are illegal even if those defaults are never employed. This is not necessarily a bug. -- The phasing of trait definitions before type definitions does not - seem to be necessary, sufficient, or particularly helpful, given that - processing a trait definition can trigger processing a type def and - vice versa. However, if I remove it, I get ICEs, so some more work is - needed in that area. -nmatsakis */ @@ -82,6 +76,7 @@ use middle::ty::fold::{TypeFolder, TypeFoldable}; use middle::ty::util::IntTypeExt; use middle::infer; use rscope::*; +use rustc::dep_graph::DepNode; use rustc::front::map as hir_map; use util::common::{ErrorReported, memoized}; use util::nodemap::{FnvHashMap, FnvHashSet}; @@ -107,9 +102,6 @@ use rustc_front::print::pprust; pub fn collect_item_types(tcx: &ty::ctxt) { let ccx = &CrateCtxt { tcx: tcx, stack: RefCell::new(Vec::new()) }; - let mut visitor = CollectTraitDefVisitor{ ccx: ccx }; - ccx.tcx.map.krate().visit_all_items(&mut visitor); - let mut visitor = CollectItemTypesVisitor{ ccx: ccx }; ccx.tcx.map.krate().visit_all_items(&mut visitor); } @@ -149,41 +141,17 @@ enum AstConvRequest { } /////////////////////////////////////////////////////////////////////////// -// First phase: just collect *trait definitions* -- basically, the set -// of type parameters and supertraits. This is information we need to -// know later when parsing field defs. - -struct CollectTraitDefVisitor<'a, 'tcx: 'a> { - ccx: &'a CrateCtxt<'a, 'tcx> -} - -impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for CollectTraitDefVisitor<'a, 'tcx> { - fn visit_item(&mut self, i: &hir::Item) { - match i.node { - hir::ItemTrait(..) => { - // computing the trait def also fills in the table - let _ = trait_def_of_item(self.ccx, i); - } - _ => { } - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Second phase: collection proper. struct CollectItemTypesVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> } impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for CollectItemTypesVisitor<'a, 'tcx> { - fn visit_item(&mut self, i: &hir::Item) { - convert_item(self.ccx, i); - intravisit::walk_item(self, i); - } - fn visit_foreign_item(&mut self, i: &hir::ForeignItem) { - convert_foreign_item(self.ccx, i); - intravisit::walk_foreign_item(self, i); + fn visit_item(&mut self, item: &hir::Item) { + let tcx = self.ccx.tcx; + let item_def_id = tcx.map.local_def_id(item.id); + let _task = tcx.dep_graph.in_task(DepNode::CollectItem(item_def_id)); + convert_item(self.ccx, item); } } @@ -742,8 +710,12 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { debug!("convert: item {} with id {}", it.name, it.id); match it.node { // These don't define types. - hir::ItemExternCrate(_) | hir::ItemUse(_) | - hir::ItemForeignMod(_) | hir::ItemMod(_) => { + hir::ItemExternCrate(_) | hir::ItemUse(_) | hir::ItemMod(_) => { + } + hir::ItemForeignMod(ref foreign_mod) => { + for item in &foreign_mod.items { + convert_foreign_item(ccx, item); + } } hir::ItemEnum(ref enum_definition, _) => { let (scheme, predicates) = convert_typed_item(ccx, it); @@ -1532,6 +1504,11 @@ fn type_scheme_of_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &hir::Item) -> ty::TypeScheme<'tcx> { + // Computing the type scheme of an item is a discrete task: + let item_def_id = ccx.tcx.map.local_def_id(it.id); + let _task = ccx.tcx.dep_graph.in_task(DepNode::TypeScheme(item_def_id)); + ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); // we have access to `it` + memoized(&ccx.tcx.tcache, ccx.tcx.map.local_def_id(it.id), |_| compute_type_scheme_of_item(ccx, it)) @@ -1648,13 +1625,18 @@ fn convert_typed_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, fn type_scheme_of_foreign_item<'a, 'tcx>( ccx: &CrateCtxt<'a, 'tcx>, - it: &hir::ForeignItem, + item: &hir::ForeignItem, abi: abi::Abi) -> ty::TypeScheme<'tcx> { + // Computing the type scheme of a foreign item is a discrete task: + let item_def_id = ccx.tcx.map.local_def_id(item.id); + let _task = ccx.tcx.dep_graph.in_task(DepNode::TypeScheme(item_def_id)); + ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); // we have access to `item` + memoized(&ccx.tcx.tcache, - ccx.tcx.map.local_def_id(it.id), - |_| compute_type_scheme_of_foreign_item(ccx, it, abi)) + ccx.tcx.map.local_def_id(item.id), + |_| compute_type_scheme_of_foreign_item(ccx, item, abi)) } fn compute_type_scheme_of_foreign_item<'a, 'tcx>( diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 495b8995ceea2..1943d32a091af 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -94,6 +94,7 @@ extern crate rustc_platform_intrinsics as intrinsics; extern crate rustc_front; extern crate rustc_back; +pub use rustc::dep_graph; pub use rustc::front; pub use rustc::lint; pub use rustc::middle; diff --git a/src/librustc_typeck/variance.rs b/src/librustc_typeck/variance.rs index 1fafe3484f0f2..02fc62aff86f4 100644 --- a/src/librustc_typeck/variance.rs +++ b/src/librustc_typeck/variance.rs @@ -266,6 +266,7 @@ use self::ParamKind::*; use arena; use arena::TypedArena; +use dep_graph::DepNode; use middle::def_id::DefId; use middle::resolve_lifetime as rl; use middle::subst; @@ -280,6 +281,7 @@ use rustc_front::intravisit::Visitor; use util::nodemap::NodeMap; pub fn infer_variance(tcx: &ty::ctxt) { + let _task = tcx.dep_graph.in_task(DepNode::Variance); let krate = tcx.map.krate(); let mut arena = arena::TypedArena::new(); let terms_cx = determine_parameters_to_be_inferred(tcx, &mut arena, krate); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 1ccab1b16ebdb..33bf03d24cf6c 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -152,6 +152,7 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec, externs: Externs, &name, resolve::MakeGlobMap::No, |tcx, _, analysis| { + let _ignore = tcx.dep_graph.in_ignore(); let ty::CrateAnalysis { access_levels, .. } = analysis; // Convert from a NodeId set to a DefId set since we don't always have easy access diff --git a/src/test/compile-fail/dep-graph-caller-callee.rs b/src/test/compile-fail/dep-graph-caller-callee.rs new file mode 100644 index 0000000000000..70ec6e1c0ca9d --- /dev/null +++ b/src/test/compile-fail/dep-graph-caller-callee.rs @@ -0,0 +1,47 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that two unrelated functions have no trans dependency. + +#![feature(rustc_attrs)] +#![allow(unused_attributes)] +#![allow(dead_code)] + +fn main() { } + +mod x { + #[rustc_if_this_changed] + //~^ ERROR no path to TypeckItemBody for `z::z` + //~| ERROR no path to TransCrateItem for `z::z` + pub fn x() { } +} + +mod y { + use x; + + // These dependencies SHOULD exist: + #[rustc_then_this_would_need(TypeckItemBody)] + #[rustc_then_this_would_need(TransCrateItem)] + pub fn y() { + x::x(); + } +} + +mod z { + use y; + + // These are expected to yield errors, because changes to `x` + // affect the BODY of `y`, but not its signature. + #[rustc_then_this_would_need(TypeckItemBody)] + #[rustc_then_this_would_need(TransCrateItem)] + pub fn z() { + y::y(); + } +} diff --git a/src/test/compile-fail/dep-graph-unrelated.rs b/src/test/compile-fail/dep-graph-unrelated.rs new file mode 100644 index 0000000000000..b54e32702c06d --- /dev/null +++ b/src/test/compile-fail/dep-graph-unrelated.rs @@ -0,0 +1,21 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that two unrelated functions have no trans dependency. + +#![feature(rustc_attrs)] +#![allow(unused_attributes)] +#![allow(dead_code)] + +#[rustc_if_this_changed] //~ ERROR no path to TransCrateItem for `bar` +fn main() { } + +#[rustc_then_this_would_need(TransCrateItem)] +fn bar() { }