diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index f35d5f25962..67c52bb6c36 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -829,6 +829,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, index, name, |tcx| { + time(time_passes, + "load_dep_graph", + || rustc_incremental::load_dep_graph(tcx)); + // passes are timed inside typeck try_with_f!(typeck::check_crate(tcx, trait_map), (tcx, None, analysis)); @@ -962,6 +966,10 @@ pub fn phase_4_translate_to_llvm<'tcx>(tcx: &TyCtxt<'tcx>, "assert dep graph", move || rustc_incremental::assert_dep_graph(tcx)); + time(time_passes, + "serialize dep graph", + move || rustc_incremental::save_dep_graph(tcx)); + translation } diff --git a/src/librustc_incremental/persist/data.rs b/src/librustc_incremental/persist/data.rs new file mode 100644 index 00000000000..8be8bd60b23 --- /dev/null +++ b/src/librustc_incremental/persist/data.rs @@ -0,0 +1,35 @@ +// Copyright 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. + +//! The data that we will serialize and deserialize. + +use rustc::dep_graph::DepNode; +use rustc_serialize::{Decoder as RustcDecoder, + Encodable as RustcEncodable, Encoder as RustcEncoder}; + +use super::directory::DefPathIndex; + +#[derive(Debug, RustcEncodable, RustcDecodable)] +pub struct SerializedDepGraph { + pub nodes: Vec>, + pub edges: Vec, + pub hashes: Vec, +} + +pub type SerializedEdge = (DepNode, DepNode); + +#[derive(Debug, RustcEncodable, RustcDecodable)] +pub struct SerializedHash { + pub index: DefPathIndex, + + /// the hash itself, computed by `calculate_item_hash` + pub hash: u64, +} + diff --git a/src/librustc_incremental/persist/directory.rs b/src/librustc_incremental/persist/directory.rs new file mode 100644 index 00000000000..f4d155962d1 --- /dev/null +++ b/src/librustc_incremental/persist/directory.rs @@ -0,0 +1,118 @@ +// Copyright 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. + +//! Code to convert a DefId into a DefPath (when serializing) and then +//! back again (when deserializing). Note that the new DefId +//! necessarily will not be the same as the old (and of course the +//! item might even be removed in the meantime). + +use rustc::dep_graph::DepNode; +use rustc::front::map::DefPath; +use rustc::middle::def_id::DefId; +use rustc::ty; +use rustc::util::nodemap::DefIdMap; +use rustc_serialize::{Decoder as RustcDecoder, + Encodable as RustcEncodable, Encoder as RustcEncoder}; +use std::fmt::{self, Debug}; + +/// Index into the DefIdDirectory +#[derive(Copy, Clone, Debug, PartialOrd, Ord, Hash, PartialEq, Eq, + RustcEncodable, RustcDecodable)] +pub struct DefPathIndex { + index: u32 +} + +#[derive(RustcEncodable, RustcDecodable)] +pub struct DefIdDirectory { + // N.B. don't use Removable here because these def-ids are loaded + // directly without remapping, so loading them should not fail. + paths: Vec +} + +impl DefIdDirectory { + pub fn new() -> DefIdDirectory { + DefIdDirectory { paths: vec![] } + } + + pub fn retrace(&self, tcx: &ty::TyCtxt) -> RetracedDefIdDirectory { + let ids = self.paths.iter() + .map(|path| tcx.map.retrace_path(path)) + .collect(); + RetracedDefIdDirectory { ids: ids } + } +} + +#[derive(Debug, RustcEncodable, RustcDecodable)] +pub struct RetracedDefIdDirectory { + ids: Vec> +} + +impl RetracedDefIdDirectory { + pub fn def_id(&self, index: DefPathIndex) -> Option { + self.ids[index.index as usize] + } + + pub fn map(&self, node: DepNode) -> Option> { + node.map_def(|&index| self.def_id(index)) + } +} + +pub struct DefIdDirectoryBuilder<'a,'tcx:'a> { + tcx: &'a ty::TyCtxt<'tcx>, + hash: DefIdMap>, + directory: DefIdDirectory, +} + +impl<'a,'tcx> DefIdDirectoryBuilder<'a,'tcx> { + pub fn new(tcx: &'a ty::TyCtxt<'tcx>) -> DefIdDirectoryBuilder<'a, 'tcx> { + DefIdDirectoryBuilder { + tcx: tcx, + hash: DefIdMap(), + directory: DefIdDirectory::new() + } + } + + pub fn add(&mut self, def_id: DefId) -> Option { + if !def_id.is_local() { + // FIXME(#32015) clarify story about cross-crate dep tracking + return None; + } + + let tcx = self.tcx; + let paths = &mut self.directory.paths; + self.hash.entry(def_id) + .or_insert_with(|| { + let def_path = tcx.def_path(def_id); + if !def_path.is_local() { + return None; + } + let index = paths.len() as u32; + paths.push(def_path); + Some(DefPathIndex { index: index }) + }) + .clone() + } + + pub fn map(&mut self, node: DepNode) -> Option> { + node.map_def(|&def_id| self.add(def_id)) + } + + pub fn into_directory(self) -> DefIdDirectory { + self.directory + } +} + +impl Debug for DefIdDirectory { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fmt.debug_list() + .entries(self.paths.iter().enumerate()) + .finish() + } +} diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs new file mode 100644 index 00000000000..0aeddd5b7c8 --- /dev/null +++ b/src/librustc_incremental/persist/dirty_clean.rs @@ -0,0 +1,151 @@ +// Copyright 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. + +//! Debugging code to test the state of the dependency graph just +//! after it is loaded from disk. For each node marked with +//! `#[rustc_clean]` or `#[rustc_dirty]`, we will check that a +//! suitable node for that item either appears or does not appear in +//! the dep-graph, as appropriate: +//! +//! - `#[rustc_dirty(label="TypeckItemBody", cfg="rev2")]` if we are +//! in `#[cfg(rev2)]`, then there MUST NOT be a node +//! `DepNode::TypeckItemBody(X)` where `X` is the def-id of the +//! current node. +//! - `#[rustc_clean(label="TypeckItemBody", cfg="rev2")]` same as above, +//! except that the node MUST exist. +//! +//! Errors are reported if we are in the suitable configuration but +//! the required condition is not met. + +use rustc::dep_graph::{DepGraphQuery, DepNode}; +use rustc::middle::def_id::DefId; +use rustc_front::hir; +use rustc_front::intravisit::Visitor; +use syntax::ast::{self, Attribute, MetaItem}; +use syntax::attr::AttrMetaMethods; +use syntax::parse::token::InternedString; +use rustc::ty; + +const DIRTY: &'static str = "rustc_dirty"; +const CLEAN: &'static str = "rustc_clean"; +const LABEL: &'static str = "label"; +const CFG: &'static str = "cfg"; + +pub fn check_dirty_clean_annotations(tcx: &ty::TyCtxt) { + let _ignore = tcx.dep_graph.in_ignore(); + let query = tcx.dep_graph.query(); + let krate = tcx.map.krate(); + krate.visit_all_items(&mut DirtyCleanVisitor { + tcx: tcx, + query: &query, + }); +} + +pub struct DirtyCleanVisitor<'a, 'tcx:'a> { + tcx: &'a ty::TyCtxt<'tcx>, + query: &'a DepGraphQuery, +} + +impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> { + fn expect_associated_value(&self, item: &MetaItem) -> InternedString { + if let Some(value) = item.value_str() { + value + } else { + self.tcx.sess.span_fatal( + item.span, + &format!("associated value expected for `{}`", item.name())); + } + } + + /// Given a `#[rustc_dirty]` or `#[rustc_clean]` attribute, scan + /// for a `cfg="foo"` attribute and check whether we have a cfg + /// flag called `foo`. + fn check_config(&self, attr: &ast::Attribute) -> bool { + debug!("check_config(attr={:?})", attr); + let config = &self.tcx.map.krate().config; + debug!("check_config: config={:?}", config); + for item in attr.meta_item_list().unwrap_or(&[]) { + if item.check_name(CFG) { + let value = self.expect_associated_value(item); + debug!("check_config: searching for cfg {:?}", value); + for cfg in &config[..] { + if cfg.check_name(&value[..]) { + debug!("check_config: matched {:?}", cfg); + return true; + } + } + } + } + debug!("check_config: no match found"); + return false; + } + + fn dep_node(&self, attr: &Attribute, def_id: DefId) -> DepNode { + for item in attr.meta_item_list().unwrap_or(&[]) { + if item.check_name(LABEL) { + let value = self.expect_associated_value(item); + match DepNode::from_label_string(&value[..], def_id) { + Ok(def_id) => return def_id, + Err(()) => { + self.tcx.sess.span_fatal( + item.span, + &format!("dep-node label `{}` not recognized", value)); + } + } + } + } + + self.tcx.sess.span_fatal(attr.span, "no `label` found"); + } + + fn dep_node_str(&self, dep_node: DepNode) -> DepNode { + dep_node.map_def(|&def_id| Some(self.tcx.item_path_str(def_id))).unwrap() + } + + fn assert_dirty(&self, item: &hir::Item, dep_node: DepNode) { + debug!("assert_dirty({:?})", dep_node); + + if self.query.contains_node(&dep_node) { + let dep_node_str = self.dep_node_str(dep_node); + self.tcx.sess.span_err( + item.span, + &format!("`{:?}` found in dep graph, but should be dirty", dep_node_str)); + } + } + + fn assert_clean(&self, item: &hir::Item, dep_node: DepNode) { + debug!("assert_clean({:?})", dep_node); + + if !self.query.contains_node(&dep_node) { + let dep_node_str = self.dep_node_str(dep_node); + self.tcx.sess.span_err( + item.span, + &format!("`{:?}` not found in dep graph, but should be clean", dep_node_str)); + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for DirtyCleanVisitor<'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(DIRTY) { + if self.check_config(attr) { + self.assert_dirty(item, self.dep_node(attr, def_id)); + } + } else if attr.check_name(CLEAN) { + if self.check_config(attr) { + self.assert_clean(item, self.dep_node(attr, def_id)); + } + } + } + } +} + diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs new file mode 100644 index 00000000000..588113e2eaa --- /dev/null +++ b/src/librustc_incremental/persist/load.rs @@ -0,0 +1,221 @@ +// Copyright 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. + +//! Code to save/load the dep-graph from files. + + +use calculate_svh::SvhCalculate; +use rbml::{self, Doc}; +use rbml::reader::{self, DecodeResult, Decoder}; +use rustc::dep_graph::DepNode; +use rustc::middle::def_id::DefId; +use rustc::ty; +use rustc_data_structures::fnv::FnvHashSet; +use rustc_serialize::Decodable as RustcDecodable; +use std::io::Read; +use std::fs::File; +use std::path::Path; + +use super::data::*; +use super::directory::*; +use super::dirty_clean; +use super::util::*; + +type DirtyNodes = FnvHashSet>; + +type CleanEdges = Vec<(DepNode, DepNode)>; + +pub fn load_dep_graph<'tcx>(tcx: &ty::TyCtxt<'tcx>) { + let _ignore = tcx.dep_graph.in_ignore(); + + if let Some(dep_graph) = dep_graph_path(tcx) { + load_dep_graph_if_exists(tcx, &dep_graph); + dirty_clean::check_dirty_clean_annotations(tcx); + } +} + +pub fn load_dep_graph_if_exists<'tcx>(tcx: &ty::TyCtxt<'tcx>, path: &Path) { + if !path.exists() { + return; + } + + let mut data = vec![]; + match + File::open(path) + .and_then(|mut file| file.read_to_end(&mut data)) + { + Ok(_) => { } + Err(err) => { + tcx.sess.err( + &format!("could not load dep-graph from `{}`: {}", + path.display(), err)); + return; + } + } + + match decode_dep_graph(tcx, Doc::new(&data)) { + Ok(dirty) => dirty, + Err(err) => { + tcx.sess.bug( + &format!("decoding error in dep-graph from `{}`: {}", + path.display(), err)); + } + } +} + +pub fn decode_dep_graph<'tcx, 'doc>(tcx: &ty::TyCtxt<'tcx>, doc: rbml::Doc<'doc>) + -> DecodeResult<()> +{ + // First load the directory, which maps the def-ids found + // elsewhere into `DefPath`. We can then refresh the `DefPath` to + // obtain updated def-ids. + let directory = { + let directory_doc = reader::get_doc(doc, DIRECTORY_TAG); + let mut decoder = Decoder::new(directory_doc); + try!(DefIdDirectory::decode(&mut decoder)) + }; + + debug!("decode_dep_graph: directory = {:#?}", directory); + + // Retrace those paths to find their current location (if any). + let retraced = directory.retrace(tcx); + + debug!("decode_dep_graph: retraced = {:#?}", retraced); + + // Deserialize the dep-graph (which will include DefPathIndex entries) + let serialized_dep_graph = { + let dep_graph_doc = reader::get_doc(doc, DEP_GRAPH_TAG); + let mut decoder = Decoder::new(dep_graph_doc); + try!(SerializedDepGraph::decode(&mut decoder)) + }; + + debug!("decode_dep_graph: serialized_dep_graph = {:#?}", serialized_dep_graph); + + // Compute the set of Hir nodes whose data has changed. + let mut dirty_nodes = + initial_dirty_nodes(tcx, &serialized_dep_graph.hashes, &retraced); + + debug!("decode_dep_graph: initial dirty_nodes = {:#?}", dirty_nodes); + + // Find all DepNodes reachable from that core set. This loop + // iterates repeatedly over the list of edges whose source is not + // known to be dirty (`clean_edges`). If it finds an edge whose + // source is dirty, it removes it from that list and adds the + // target to `dirty_nodes`. It stops when it reaches a fixed + // point. + let clean_edges = compute_clean_edges(&serialized_dep_graph.edges, + &retraced, + &mut dirty_nodes); + + // Add synthetic `foo->foo` edges for each clean node `foo` that + // we had before. This is sort of a hack to create clean nodes in + // the graph, since the existence of a node is a signal that the + // work it represents need not be repeated. + let clean_nodes = + serialized_dep_graph.nodes + .iter() + .filter_map(|&node| retraced.map(node)) + .filter(|node| !dirty_nodes.contains(node)) + .map(|node| (node, node)); + + // Add nodes and edges that are not dirty into our main graph. + let dep_graph = tcx.dep_graph.clone(); + for (source, target) in clean_edges.into_iter().chain(clean_nodes) { + let _task = dep_graph.in_task(target); + dep_graph.read(source); + + debug!("decode_dep_graph: clean edge: {:?} -> {:?}", source, target); + } + + Ok(()) +} + +fn initial_dirty_nodes<'tcx>(tcx: &ty::TyCtxt<'tcx>, + hashed_items: &[SerializedHash], + retraced: &RetracedDefIdDirectory) + -> DirtyNodes { + let mut items_removed = false; + let mut dirty_nodes = FnvHashSet(); + for hashed_item in hashed_items { + match retraced.def_id(hashed_item.index) { + Some(def_id) => { + let current_hash = tcx.calculate_item_hash(def_id); + debug!("initial_dirty_nodes: hash of {:?} is {:?}, was {:?}", + def_id, current_hash, hashed_item.hash); + if current_hash != hashed_item.hash { + dirty_nodes.insert(DepNode::Hir(def_id)); + } + } + None => { + items_removed = true; + } + } + } + + // If any of the items in the krate have changed, then we consider + // the meta-node `Krate` to be dirty, since that means something + // which (potentially) read the contents of every single item. + if items_removed || !dirty_nodes.is_empty() { + dirty_nodes.insert(DepNode::Krate); + } + + dirty_nodes +} + +fn compute_clean_edges(serialized_edges: &[(SerializedEdge)], + retraced: &RetracedDefIdDirectory, + dirty_nodes: &mut DirtyNodes) + -> CleanEdges { + // Build up an initial list of edges. Include an edge (source, + // target) if neither node has been removed. If the source has + // been removed, add target to the list of dirty nodes. + let mut clean_edges = Vec::with_capacity(serialized_edges.len()); + for &(serialized_source, serialized_target) in serialized_edges { + if let Some(target) = retraced.map(serialized_target) { + if let Some(source) = retraced.map(serialized_source) { + clean_edges.push((source, target)) + } else { + // source removed, target must be dirty + dirty_nodes.insert(target); + } + } else { + // target removed, ignore the edge + } + } + + debug!("compute_clean_edges: dirty_nodes={:#?}", dirty_nodes); + + // Propagate dirty marks by iterating repeatedly over + // `clean_edges`. If we find an edge `(source, target)` where + // `source` is dirty, add `target` to the list of dirty nodes and + // remove it. Keep doing this until we find no more dirty nodes. + let mut previous_size = 0; + while dirty_nodes.len() > previous_size { + debug!("compute_clean_edges: previous_size={}", previous_size); + previous_size = dirty_nodes.len(); + let mut i = 0; + while i < clean_edges.len() { + if dirty_nodes.contains(&clean_edges[i].0) { + let (source, target) = clean_edges.swap_remove(i); + debug!("compute_clean_edges: dirty source {:?} -> {:?}", + source, target); + dirty_nodes.insert(target); + } else if dirty_nodes.contains(&clean_edges[i].1) { + let (source, target) = clean_edges.swap_remove(i); + debug!("compute_clean_edges: dirty target {:?} -> {:?}", + source, target); + } else { + i += 1; + } + } + } + + clean_edges +} diff --git a/src/librustc_incremental/persist/mod.rs b/src/librustc_incremental/persist/mod.rs new file mode 100644 index 00000000000..8d04fd30a19 --- /dev/null +++ b/src/librustc_incremental/persist/mod.rs @@ -0,0 +1,23 @@ +// 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. + +//! When in incremental mode, this pass dumps out the dependency graph +//! into the given directory. At the same time, it also hashes the +//! various HIR nodes. + +mod data; +mod directory; +mod dirty_clean; +mod load; +mod save; +mod util; + +pub use self::load::load_dep_graph; +pub use self::save::save_dep_graph; diff --git a/src/librustc_incremental/persist/save.rs b/src/librustc_incremental/persist/save.rs new file mode 100644 index 00000000000..23889c7bbfc --- /dev/null +++ b/src/librustc_incremental/persist/save.rs @@ -0,0 +1,142 @@ +// Copyright 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. + +use calculate_svh::SvhCalculate; +use rbml::writer::{EncodeResult, Encoder}; +use rustc::dep_graph::DepNode; +use rustc::ty; +use rustc_serialize::{Encodable as RustcEncodable}; +use std::io::{Cursor, Write}; +use std::fs::{self, File}; + +use super::data::*; +use super::directory::*; +use super::util::*; + +pub fn save_dep_graph<'tcx>(tcx: &ty::TyCtxt<'tcx>) { + let _ignore = tcx.dep_graph.in_ignore(); + + if let Some(dep_graph) = dep_graph_path(tcx) { + // delete the old dep-graph, if any + if dep_graph.exists() { + match fs::remove_file(&dep_graph) { + Ok(()) => { } + Err(err) => { + tcx.sess.err( + &format!("unable to delete old dep-graph at `{}`: {}", + dep_graph.display(), err)); + return; + } + } + } + + // generate the data in a memory buffer + let mut wr = Cursor::new(Vec::new()); + match encode_dep_graph(tcx, &mut Encoder::new(&mut wr)) { + Ok(()) => { } + Err(err) => { + tcx.sess.err( + &format!("could not encode dep-graph to `{}`: {}", + dep_graph.display(), err)); + return; + } + } + + // write the data out + let data = wr.into_inner(); + match + File::create(&dep_graph) + .and_then(|mut file| file.write_all(&data)) + { + Ok(_) => { } + Err(err) => { + tcx.sess.err( + &format!("failed to write dep-graph to `{}`: {}", + dep_graph.display(), err)); + return; + } + } + } +} + +pub fn encode_dep_graph<'tcx>(tcx: &ty::TyCtxt<'tcx>, + encoder: &mut Encoder) + -> EncodeResult +{ + // Here we take advantage of how RBML allows us to skip around + // and encode the depgraph as a two-part structure: + // + // ``` + // [SerializedDepGraph] // tag 0 + // [DefIdDirectory] // tag 1 + // ``` + // + // Then later we can load the directory by skipping to find tag 1. + + let query = tcx.dep_graph.query(); + + let mut builder = DefIdDirectoryBuilder::new(tcx); + + // Create hashes for things we can persist. + let hashes = + query.nodes() + .into_iter() + .filter_map(|dep_node| match dep_node { + DepNode::Hir(def_id) => { + assert!(def_id.is_local()); + builder.add(def_id) + .map(|index| { + let hash = tcx.calculate_item_hash(def_id); + SerializedHash { index: index, hash: hash } + }) + } + _ => None + }) + .collect(); + + // Create the serialized dep-graph, dropping nodes that are + // from other crates or from inlined items. + // + // FIXME(#32015) fix handling of other crates + let graph = SerializedDepGraph { + nodes: query.nodes().into_iter() + .flat_map(|node| builder.map(node)) + .collect(), + edges: query.edges().into_iter() + .flat_map(|(source_node, target_node)| { + builder.map(source_node) + .and_then(|source| { + builder.map(target_node) + .map(|target| (source, target)) + }) + }) + .collect(), + hashes: hashes, + }; + + debug!("graph = {:#?}", graph); + + // Encode the graph data into RBML. + try!(encoder.start_tag(DEP_GRAPH_TAG)); + try!(graph.encode(encoder)); + try!(encoder.end_tag()); + + // Now encode the directory. + let directory = builder.into_directory(); + + debug!("directory = {:#?}", directory); + + try!(encoder.start_tag(DIRECTORY_TAG)); + try!(directory.encode(encoder)); + try!(encoder.end_tag()); + + Ok(()) +} + diff --git a/src/librustc_incremental/persist/serialize.rs b/src/librustc_incremental/persist/serialize.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/librustc_incremental/persist/util.rs b/src/librustc_incremental/persist/util.rs new file mode 100644 index 00000000000..11cc55950ca --- /dev/null +++ b/src/librustc_incremental/persist/util.rs @@ -0,0 +1,36 @@ +// Copyright 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. + +use rustc::ty; +use std::fs; +use std::path::PathBuf; + +pub const DEP_GRAPH_TAG: usize = 0x100; + +pub const DIRECTORY_TAG: usize = DEP_GRAPH_TAG + 1; + +pub fn dep_graph_path<'tcx>(tcx: &ty::TyCtxt<'tcx>) -> Option { + // For now, just save/load dep-graph from + // directory/dep_graph.rbml + tcx.sess.opts.incremental.as_ref().and_then(|incr_dir| { + match fs::create_dir_all(&incr_dir){ + Ok(()) => {} + Err(err) => { + tcx.sess.err( + &format!("could not create the directory `{}`: {}", + incr_dir.display(), err)); + return None; + } + } + + Some(incr_dir.join("dep_graph.rbml")) + }) +} + diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 5ade636a327..f08d3c685bb 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -356,6 +356,14 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat "the `#[rustc_if_this_changed]` attribute \ is just used for rustc unit tests \ and will never be stable")), + ("rustc_dirty", Whitelisted, Gated("rustc_attrs", + "the `#[rustc_dirty]` attribute \ + is just used for rustc unit tests \ + and will never be stable")), + ("rustc_clean", Whitelisted, Gated("rustc_attrs", + "the `#[rustc_clean]` attribute \ + is just used for rustc unit tests \ + and will never be stable")), ("rustc_symbol_name", Whitelisted, Gated("rustc_attrs", "internal rustc attributes will never be stable")), ("rustc_item_path", Whitelisted, Gated("rustc_attrs",