|
8 | 8 | // option. This file may not be copied, modified, or distributed
|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
11 |
| -//! Calculation of a Strict Version Hash for crates. For a length |
12 |
| -//! comment explaining the general idea, see `librustc/middle/svh.rs`. |
13 |
| -
|
| 11 | +//! Calculation of the (misnamed) "strict version hash" for crates and |
| 12 | +//! items. This hash is used to tell when the HIR changed in such a |
| 13 | +//! way that results from previous compilations may no longer be |
| 14 | +//! applicable and hence must be recomputed. It should probably be |
| 15 | +//! renamed to the ICH (incremental compilation hash). |
| 16 | +//! |
| 17 | +//! The hashes for all items are computed once at the beginning of |
| 18 | +//! compilation and stored into a map. In addition, a hash is computed |
| 19 | +//! of the **entire crate**. |
| 20 | +//! |
| 21 | +//! Storing the hashes in a map avoids the need to compute them twice |
| 22 | +//! (once when loading prior incremental results and once when |
| 23 | +//! saving), but it is also important for correctness: at least as of |
| 24 | +//! the time of this writing, the typeck passes rewrites entries in |
| 25 | +//! the dep-map in-place to accommodate UFCS resolutions. Since name |
| 26 | +//! resolution is part of the hash, the result is that hashes computed |
| 27 | +//! at the end of compilation would be different from those computed |
| 28 | +//! at the beginning. |
| 29 | +
|
| 30 | +use syntax::ast; |
14 | 31 | use syntax::attr::AttributeMethods;
|
15 | 32 | use std::hash::{Hash, SipHasher, Hasher};
|
| 33 | +use rustc::dep_graph::DepNode; |
| 34 | +use rustc::hir; |
16 | 35 | use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId};
|
17 |
| -use rustc::hir::map::{NodeItem, NodeForeignItem}; |
18 |
| -use rustc::hir::svh::Svh; |
| 36 | +use rustc::hir::intravisit as visit; |
19 | 37 | use rustc::ty::TyCtxt;
|
20 |
| -use rustc::hir::intravisit::{self, Visitor}; |
| 38 | +use rustc_data_structures::fnv::FnvHashMap; |
21 | 39 |
|
22 | 40 | use self::svh_visitor::StrictVersionHashVisitor;
|
23 | 41 |
|
24 | 42 | mod svh_visitor;
|
25 | 43 |
|
26 |
| -pub trait SvhCalculate { |
27 |
| - /// Calculate the SVH for an entire krate. |
28 |
| - fn calculate_krate_hash(self) -> Svh; |
| 44 | +pub type HashesMap = FnvHashMap<DepNode<DefId>, u64>; |
29 | 45 |
|
30 |
| - /// Calculate the SVH for a particular item. |
31 |
| - fn calculate_item_hash(self, def_id: DefId) -> u64; |
| 46 | +pub fn compute_hashes_map<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> HashesMap { |
| 47 | + let _ignore = tcx.dep_graph.in_ignore(); |
| 48 | + let krate = tcx.map.krate(); |
| 49 | + let mut visitor = HashItemsVisitor { tcx: tcx, hashes: FnvHashMap() }; |
| 50 | + visitor.calculate_def_id(DefId::local(CRATE_DEF_INDEX), |v| visit::walk_crate(v, krate)); |
| 51 | + krate.visit_all_items(&mut visitor); |
| 52 | + visitor.compute_crate_hash(); |
| 53 | + visitor.hashes |
32 | 54 | }
|
33 | 55 |
|
34 |
| -impl<'a, 'tcx> SvhCalculate for TyCtxt<'a, 'tcx, 'tcx> { |
35 |
| - fn calculate_krate_hash(self) -> Svh { |
36 |
| - // FIXME (#14132): This is better than it used to be, but it still not |
37 |
| - // ideal. We now attempt to hash only the relevant portions of the |
38 |
| - // Crate AST as well as the top-level crate attributes. (However, |
39 |
| - // the hashing of the crate attributes should be double-checked |
40 |
| - // to ensure it is not incorporating implementation artifacts into |
41 |
| - // the hash that are not otherwise visible.) |
| 56 | +struct HashItemsVisitor<'a, 'tcx: 'a> { |
| 57 | + tcx: TyCtxt<'a, 'tcx, 'tcx>, |
| 58 | + hashes: HashesMap, |
| 59 | +} |
42 | 60 |
|
43 |
| - let crate_disambiguator = self.sess.local_crate_disambiguator(); |
44 |
| - let krate = self.map.krate(); |
| 61 | +impl<'a, 'tcx> HashItemsVisitor<'a, 'tcx> { |
| 62 | + fn calculate_node_id<W>(&mut self, id: ast::NodeId, walk_op: W) |
| 63 | + where W: for<'v> FnMut(&mut StrictVersionHashVisitor<'v, 'tcx>) |
| 64 | + { |
| 65 | + let def_id = self.tcx.map.local_def_id(id); |
| 66 | + self.calculate_def_id(def_id, walk_op) |
| 67 | + } |
45 | 68 |
|
46 |
| - // FIXME: this should use SHA1, not SipHash. SipHash is not built to |
47 |
| - // avoid collisions. |
| 69 | + fn calculate_def_id<W>(&mut self, def_id: DefId, mut walk_op: W) |
| 70 | + where W: for<'v> FnMut(&mut StrictVersionHashVisitor<'v, 'tcx>) |
| 71 | + { |
| 72 | + assert!(def_id.is_local()); |
| 73 | + debug!("HashItemsVisitor::calculate(def_id={:?})", def_id); |
| 74 | + // FIXME: this should use SHA1, not SipHash. SipHash is not |
| 75 | + // built to avoid collisions. |
48 | 76 | let mut state = SipHasher::new();
|
49 |
| - debug!("state: {:?}", state); |
| 77 | + walk_op(&mut StrictVersionHashVisitor::new(&mut state, self.tcx)); |
| 78 | + let item_hash = state.finish(); |
| 79 | + self.hashes.insert(DepNode::Hir(def_id), item_hash); |
| 80 | + debug!("calculate_item_hash: def_id={:?} hash={:?}", def_id, item_hash); |
| 81 | + } |
| 82 | + |
| 83 | + fn compute_crate_hash(&mut self) { |
| 84 | + let krate = self.tcx.map.krate(); |
50 | 85 |
|
51 |
| - // FIXME(#32753) -- at (*) we `to_le` for endianness, but is |
52 |
| - // this enough, and does it matter anyway? |
53 |
| - "crate_disambiguator".hash(&mut state); |
54 |
| - crate_disambiguator.len().to_le().hash(&mut state); // (*) |
55 |
| - crate_disambiguator.hash(&mut state); |
| 86 | + let mut crate_state = SipHasher::new(); |
56 | 87 |
|
57 |
| - debug!("crate_disambiguator: {:?}", crate_disambiguator); |
58 |
| - debug!("state: {:?}", state); |
| 88 | + let crate_disambiguator = self.tcx.sess.local_crate_disambiguator(); |
| 89 | + "crate_disambiguator".hash(&mut crate_state); |
| 90 | + crate_disambiguator.len().hash(&mut crate_state); |
| 91 | + crate_disambiguator.hash(&mut crate_state); |
59 | 92 |
|
| 93 | + // add each item (in some deterministic order) to the overall |
| 94 | + // crate hash. |
| 95 | + // |
| 96 | + // FIXME -- it'd be better to sort by the hash of the def-path, |
| 97 | + // so that reordering items would not affect the crate hash. |
60 | 98 | {
|
61 |
| - let mut visit = StrictVersionHashVisitor::new(&mut state, self); |
62 |
| - krate.visit_all_items(&mut visit); |
| 99 | + let mut keys: Vec<_> = self.hashes.keys().collect(); |
| 100 | + keys.sort(); |
| 101 | + for key in keys { |
| 102 | + self.hashes[key].hash(&mut crate_state); |
| 103 | + } |
63 | 104 | }
|
64 | 105 |
|
65 |
| - // FIXME (#14132): This hash is still sensitive to e.g. the |
66 |
| - // spans of the crate Attributes and their underlying |
67 |
| - // MetaItems; we should make ContentHashable impl for those |
68 |
| - // types and then use hash_content. But, since all crate |
69 |
| - // attributes should appear near beginning of the file, it is |
70 |
| - // not such a big deal to be sensitive to their spans for now. |
71 |
| - // |
72 |
| - // We hash only the MetaItems instead of the entire Attribute |
73 |
| - // to avoid hashing the AttrId |
74 | 106 | for attr in &krate.attrs {
|
75 | 107 | debug!("krate attr {:?}", attr);
|
76 |
| - attr.meta().hash(&mut state); |
| 108 | + attr.meta().hash(&mut crate_state); |
77 | 109 | }
|
78 | 110 |
|
79 |
| - Svh::new(state.finish()) |
| 111 | + let crate_hash = crate_state.finish(); |
| 112 | + self.hashes.insert(DepNode::Krate, crate_hash); |
| 113 | + debug!("calculate_crate_hash: crate_hash={:?}", crate_hash); |
80 | 114 | }
|
| 115 | +} |
81 | 116 |
|
82 |
| - fn calculate_item_hash(self, def_id: DefId) -> u64 { |
83 |
| - assert!(def_id.is_local()); |
84 |
| - |
85 |
| - debug!("calculate_item_hash(def_id={:?})", def_id); |
86 |
| - |
87 |
| - let mut state = SipHasher::new(); |
88 |
| - |
89 |
| - { |
90 |
| - let mut visit = StrictVersionHashVisitor::new(&mut state, self); |
91 |
| - if def_id.index == CRATE_DEF_INDEX { |
92 |
| - // the crate root itself is not registered in the map |
93 |
| - // as an item, so we have to fetch it this way |
94 |
| - let krate = self.map.krate(); |
95 |
| - intravisit::walk_crate(&mut visit, krate); |
96 |
| - } else { |
97 |
| - let node_id = self.map.as_local_node_id(def_id).unwrap(); |
98 |
| - match self.map.find(node_id) { |
99 |
| - Some(NodeItem(item)) => visit.visit_item(item), |
100 |
| - Some(NodeForeignItem(item)) => visit.visit_foreign_item(item), |
101 |
| - r => bug!("calculate_item_hash: expected an item for node {} not {:?}", |
102 |
| - node_id, r), |
103 |
| - } |
104 |
| - } |
105 |
| - } |
106 |
| - |
107 |
| - let hash = state.finish(); |
108 | 117 |
|
109 |
| - debug!("calculate_item_hash: def_id={:?} hash={:?}", def_id, hash); |
| 118 | +impl<'a, 'tcx> visit::Visitor<'tcx> for HashItemsVisitor<'a, 'tcx> { |
| 119 | + fn visit_item(&mut self, item: &'tcx hir::Item) { |
| 120 | + self.calculate_node_id(item.id, |v| v.visit_item(item)); |
| 121 | + visit::walk_item(self, item); |
| 122 | + } |
110 | 123 |
|
111 |
| - hash |
| 124 | + fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem) { |
| 125 | + self.calculate_node_id(item.id, |v| v.visit_foreign_item(item)); |
| 126 | + visit::walk_foreign_item(self, item); |
112 | 127 | }
|
113 | 128 | }
|
| 129 | + |
0 commit comments