]> git.lizzy.rs Git - rust.git/blob - src/librustc_incremental/persist/hash.rs
Rollup merge of #41910 - mersinvald:master, r=Mark-Simulacrum
[rust.git] / src / librustc_incremental / persist / hash.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use rustc::dep_graph::DepNode;
12 use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE, CRATE_DEF_INDEX};
13 use rustc::hir::svh::Svh;
14 use rustc::ich::Fingerprint;
15 use rustc::ty::TyCtxt;
16 use rustc_data_structures::fx::FxHashMap;
17 use rustc_data_structures::flock;
18 use rustc_serialize::Decodable;
19 use rustc_serialize::opaque::Decoder;
20
21 use IncrementalHashesMap;
22 use super::data::*;
23 use super::fs::*;
24 use super::file_format;
25
26 use std::hash::Hash;
27 use std::fmt::Debug;
28
29 pub struct HashContext<'a, 'tcx: 'a> {
30     pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
31     incremental_hashes_map: &'a IncrementalHashesMap,
32     item_metadata_hashes: FxHashMap<DefId, Fingerprint>,
33     crate_hashes: FxHashMap<CrateNum, Svh>,
34     global_metadata_hashes: FxHashMap<DepNode<DefId>, Fingerprint>,
35 }
36
37 impl<'a, 'tcx> HashContext<'a, 'tcx> {
38     pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
39                incremental_hashes_map: &'a IncrementalHashesMap)
40                -> Self {
41         HashContext {
42             tcx: tcx,
43             incremental_hashes_map: incremental_hashes_map,
44             item_metadata_hashes: FxHashMap(),
45             crate_hashes: FxHashMap(),
46             global_metadata_hashes: FxHashMap(),
47         }
48     }
49
50     pub fn is_hashable(dep_node: &DepNode<DefId>) -> bool {
51         match *dep_node {
52             DepNode::Krate |
53             DepNode::Hir(_) |
54             DepNode::HirBody(_) |
55             DepNode::FileMap(..) =>
56                 true,
57             DepNode::MetaData(def_id) |
58             DepNode::GlobalMetaData(def_id, _) => !def_id.is_local(),
59             _ => false,
60         }
61     }
62
63     pub fn hash(&mut self, dep_node: &DepNode<DefId>) -> Option<Fingerprint> {
64         match *dep_node {
65             DepNode::Krate => {
66                 Some(self.incremental_hashes_map[dep_node])
67             }
68
69             // HIR nodes (which always come from our crate) are an input:
70             DepNode::Hir(def_id) |
71             DepNode::HirBody(def_id) => {
72                 assert!(def_id.is_local(),
73                         "cannot hash HIR for non-local def-id {:?} => {:?}",
74                         def_id,
75                         self.tcx.item_path_str(def_id));
76
77                 Some(self.incremental_hashes_map[dep_node])
78             }
79
80             DepNode::FileMap(def_id, ref name) => {
81                 if def_id.is_local() {
82                     // We will have been able to retrace the DefId (which is
83                     // always the local CRATE_DEF_INDEX), but the file with the
84                     // given name might have been removed, so we use get() in
85                     // order to allow for that case.
86                     self.incremental_hashes_map.get(dep_node).map(|x| *x)
87                 } else {
88                     Some(self.metadata_hash(DepNode::FileMap(def_id, name.clone()),
89                                             def_id.krate,
90                                             |this| &mut this.global_metadata_hashes))
91                 }
92             }
93
94             // MetaData from other crates is an *input* to us.
95             // MetaData nodes from *our* crates are an *output*; we
96             // don't hash them, but we do compute a hash for them and
97             // save it for others to use.
98             DepNode::MetaData(def_id) if !def_id.is_local() => {
99                 Some(self.metadata_hash(def_id,
100                                         def_id.krate,
101                                         |this| &mut this.item_metadata_hashes))
102             }
103
104             DepNode::GlobalMetaData(def_id, kind) => {
105                 Some(self.metadata_hash(DepNode::GlobalMetaData(def_id, kind),
106                                         def_id.krate,
107                                         |this| &mut this.global_metadata_hashes))
108             }
109
110             _ => {
111                 // Other kinds of nodes represent computed by-products
112                 // that we don't hash directly; instead, they should
113                 // have some transitive dependency on a Hir or
114                 // MetaData node, so we'll just hash that
115                 None
116             }
117         }
118     }
119
120     fn metadata_hash<K, C>(&mut self,
121                            key: K,
122                            cnum: CrateNum,
123                            cache: C)
124                            -> Fingerprint
125         where K: Hash + Eq + Debug,
126               C: Fn(&mut Self) -> &mut FxHashMap<K, Fingerprint>,
127     {
128         debug!("metadata_hash(key={:?})", key);
129
130         debug_assert!(cnum != LOCAL_CRATE);
131         loop {
132             // check whether we have a result cached for this def-id
133             if let Some(&hash) = cache(self).get(&key) {
134                 return hash;
135             }
136
137             // check whether we did not find detailed metadata for this
138             // krate; in that case, we just use the krate's overall hash
139             if let Some(&svh) = self.crate_hashes.get(&cnum) {
140                 // micro-"optimization": avoid a cache miss if we ask
141                 // for metadata from this particular def-id again.
142                 let fingerprint = svh_to_fingerprint(svh);
143                 cache(self).insert(key, fingerprint);
144
145                 return fingerprint;
146             }
147
148             // otherwise, load the data and repeat.
149             self.load_data(cnum);
150             assert!(self.crate_hashes.contains_key(&cnum));
151         }
152     }
153
154     fn load_data(&mut self, cnum: CrateNum) {
155         debug!("load_data(cnum={})", cnum);
156
157         let svh = self.tcx.sess.cstore.crate_hash(cnum);
158         let old = self.crate_hashes.insert(cnum, svh);
159         debug!("load_data: svh={}", svh);
160         assert!(old.is_none(), "loaded data for crate {:?} twice", cnum);
161
162         if let Some(session_dir) = find_metadata_hashes_for(self.tcx, cnum) {
163             debug!("load_data: session_dir={:?}", session_dir);
164
165             // Lock the directory we'll be reading  the hashes from.
166             let lock_file_path = lock_file_path(&session_dir);
167             let _lock = match flock::Lock::new(&lock_file_path,
168                                                false,   // don't wait
169                                                false,   // don't create the lock-file
170                                                false) { // shared lock
171                 Ok(lock) => lock,
172                 Err(err) => {
173                     debug!("Could not acquire lock on `{}` while trying to \
174                             load metadata hashes: {}",
175                             lock_file_path.display(),
176                             err);
177
178                     // Could not acquire the lock. The directory is probably in
179                     // in the process of being deleted. It's OK to just exit
180                     // here. It's the same scenario as if the file had not
181                     // existed in the first place.
182                     return
183                 }
184             };
185
186             let hashes_file_path = metadata_hash_import_path(&session_dir);
187
188             match file_format::read_file(self.tcx.sess, &hashes_file_path)
189             {
190                 Ok(Some(data)) => {
191                     match self.load_from_data(cnum, &data, svh) {
192                         Ok(()) => { }
193                         Err(err) => {
194                             bug!("decoding error in dep-graph from `{}`: {}",
195                                  &hashes_file_path.display(), err);
196                         }
197                     }
198                 }
199                 Ok(None) => {
200                     // If the file is not found, that's ok.
201                 }
202                 Err(err) => {
203                     self.tcx.sess.err(
204                         &format!("could not load dep information from `{}`: {}",
205                                  hashes_file_path.display(), err));
206                 }
207             }
208         }
209     }
210
211     fn load_from_data(&mut self,
212                       cnum: CrateNum,
213                       data: &[u8],
214                       expected_svh: Svh) -> Result<(), String> {
215         debug!("load_from_data(cnum={})", cnum);
216
217         // Load up the hashes for the def-ids from this crate.
218         let mut decoder = Decoder::new(data, 0);
219         let svh_in_hashes_file = Svh::decode(&mut decoder)?;
220
221         if svh_in_hashes_file != expected_svh {
222             // We should not be able to get here. If we do, then
223             // `fs::find_metadata_hashes_for()` has messed up.
224             bug!("mismatch between SVH in crate and SVH in incr. comp. hashes")
225         }
226
227         let serialized_hashes = SerializedMetadataHashes::decode(&mut decoder)?;
228         for serialized_hash in serialized_hashes.entry_hashes {
229             // the hashes are stored with just a def-index, which is
230             // always relative to the old crate; convert that to use
231             // our internal crate number
232             let def_id = DefId { krate: cnum, index: serialized_hash.def_index };
233
234             // record the hash for this dep-node
235             let old = self.item_metadata_hashes.insert(def_id, serialized_hash.hash);
236             debug!("load_from_data: def_id={:?} hash={}", def_id, serialized_hash.hash);
237             assert!(old.is_none(), "already have hash for {:?}", def_id);
238         }
239
240         for (dep_node, fingerprint) in serialized_hashes.global_hashes {
241             // Here we need to remap the CrateNum in the DepNode.
242             let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX };
243             let dep_node = match dep_node {
244                 DepNode::GlobalMetaData(_, kind) => DepNode::GlobalMetaData(def_id, kind),
245                 DepNode::FileMap(_, name) => DepNode::FileMap(def_id, name),
246                 other => {
247                     bug!("unexpected DepNode variant: {:?}", other)
248                 }
249             };
250
251             // record the hash for this dep-node
252             debug!("load_from_data: def_node={:?} hash={}", dep_node, fingerprint);
253             let old = self.global_metadata_hashes.insert(dep_node.clone(), fingerprint);
254             assert!(old.is_none(), "already have hash for {:?}", dep_node);
255         }
256
257         Ok(())
258     }
259 }
260
261 fn svh_to_fingerprint(svh: Svh) -> Fingerprint {
262     Fingerprint::from_smaller_hash(svh.as_u64())
263 }