]> git.lizzy.rs Git - rust.git/blob - src/librustc_metadata/index_builder.rs
Rollup merge of #41249 - GuillaumeGomez:rustdoc-render, r=steveklabnik,frewsxcv
[rust.git] / src / librustc_metadata / index_builder.rs
1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Builder types for generating the "item data" section of the
12 //! metadata. This section winds up looking like this:
13 //!
14 //! ```
15 //! <common::data> // big list of item-like things...
16 //!    <common::data_item> // ...for most def-ids, there is an entry.
17 //!    </common::data_item>
18 //! </common::data>
19 //! ```
20 //!
21 //! As we generate this listing, we collect the offset of each
22 //! `data_item` entry and store it in an index. Then, when we load the
23 //! metadata, we can skip right to the metadata for a particular item.
24 //!
25 //! In addition to the offset, we need to track the data that was used
26 //! to generate the contents of each `data_item`. This is so that we
27 //! can figure out which HIR nodes contributed to that data for
28 //! incremental compilation purposes.
29 //!
30 //! The `IndexBuilder` facilitates both of these. It is created
31 //! with an `EncodingContext` (`ecx`), which it encapsulates.
32 //! It has one main method, `record()`. You invoke `record`
33 //! like so to create a new `data_item` element in the list:
34 //!
35 //! ```
36 //! index.record(some_def_id, callback_fn, data)
37 //! ```
38 //!
39 //! What record will do is to (a) record the current offset, (b) emit
40 //! the `common::data_item` tag, and then call `callback_fn` with the
41 //! given data as well as the `EncodingContext`. Once `callback_fn`
42 //! returns, the `common::data_item` tag will be closed.
43 //!
44 //! `EncodingContext` does not offer the `record` method, so that we
45 //! can ensure that `common::data_item` elements are never nested.
46 //!
47 //! In addition, while the `callback_fn` is executing, we will push a
48 //! task `MetaData(some_def_id)`, which can then observe the
49 //! reads/writes that occur in the task. For this reason, the `data`
50 //! argument that is given to the `callback_fn` must implement the
51 //! trait `DepGraphRead`, which indicates how to register reads on the
52 //! data in this new task (note that many types of data, such as
53 //! `DefId`, do not currently require any reads to be registered,
54 //! since they are not derived from a HIR node). This is also why we
55 //! give a callback fn, rather than taking a closure: it allows us to
56 //! easily control precisely what data is given to that fn.
57
58 use encoder::EncodeContext;
59 use index::Index;
60 use schema::*;
61
62 use rustc::hir;
63 use rustc::hir::def_id::DefId;
64 use rustc::ich::{StableHashingContext, Fingerprint};
65 use rustc::middle::cstore::EncodedMetadataHash;
66 use rustc::ty::TyCtxt;
67 use syntax::ast;
68
69 use std::ops::{Deref, DerefMut};
70
71 use rustc_data_structures::accumulate_vec::AccumulateVec;
72 use rustc_data_structures::stable_hasher::{StableHasher, HashStable};
73 use rustc_serialize::Encodable;
74
75 /// Builder that can encode new items, adding them into the index.
76 /// Item encoding cannot be nested.
77 pub struct IndexBuilder<'a, 'b: 'a, 'tcx: 'b> {
78     items: Index,
79     pub ecx: &'a mut EncodeContext<'b, 'tcx>,
80 }
81
82 impl<'a, 'b, 'tcx> Deref for IndexBuilder<'a, 'b, 'tcx> {
83     type Target = EncodeContext<'b, 'tcx>;
84     fn deref(&self) -> &Self::Target {
85         self.ecx
86     }
87 }
88
89 impl<'a, 'b, 'tcx> DerefMut for IndexBuilder<'a, 'b, 'tcx> {
90     fn deref_mut(&mut self) -> &mut Self::Target {
91         self.ecx
92     }
93 }
94
95 impl<'a, 'b, 'tcx> IndexBuilder<'a, 'b, 'tcx> {
96     pub fn new(ecx: &'a mut EncodeContext<'b, 'tcx>) -> Self {
97         IndexBuilder {
98             items: Index::new(ecx.tcx.hir.definitions().def_index_counts_lo_hi()),
99             ecx: ecx,
100         }
101     }
102
103     /// Emit the data for a def-id to the metadata. The function to
104     /// emit the data is `op`, and it will be given `data` as
105     /// arguments. This `record` function will call `op` to generate
106     /// the `Entry` (which may point to other encoded information)
107     /// and will then record the `Lazy<Entry>` for use in the index.
108     ///
109     /// In addition, it will setup a dep-graph task to track what data
110     /// `op` accesses to generate the metadata, which is later used by
111     /// incremental compilation to compute a hash for the metadata and
112     /// track changes.
113     ///
114     /// The reason that `op` is a function pointer, and not a closure,
115     /// is that we want to be able to completely track all data it has
116     /// access to, so that we can be sure that `DATA: DepGraphRead`
117     /// holds, and that it is therefore not gaining "secret" access to
118     /// bits of HIR or other state that would not be trackd by the
119     /// content system.
120     pub fn record<'x, DATA>(&'x mut self,
121                             id: DefId,
122                             op: fn(&mut EntryBuilder<'x, 'b, 'tcx>, DATA) -> Entry<'tcx>,
123                             data: DATA)
124         where DATA: DepGraphRead
125     {
126         assert!(id.is_local());
127         let tcx: TyCtxt<'b, 'tcx, 'tcx> = self.ecx.tcx;
128
129         // We don't track this since we are explicitly computing the incr. comp.
130         // hashes anyway. In theory we could do some tracking here and use it to
131         // avoid rehashing things (and instead cache the hashes) but it's
132         // unclear whether that would be a win since hashing is cheap enough.
133         let _task = tcx.dep_graph.in_ignore();
134
135         let compute_ich = (tcx.sess.opts.debugging_opts.query_dep_graph ||
136                            tcx.sess.opts.debugging_opts.incremental_cc) &&
137                            tcx.sess.opts.build_dep_graph();
138
139         let ecx: &'x mut EncodeContext<'b, 'tcx> = &mut *self.ecx;
140         let mut entry_builder = EntryBuilder {
141             tcx: tcx,
142             ecx: ecx,
143             hcx: if compute_ich {
144                 Some((StableHashingContext::new(tcx), StableHasher::new()))
145             } else {
146                 None
147             }
148         };
149
150         let entry = op(&mut entry_builder, data);
151
152         if let Some((ref mut hcx, ref mut hasher)) = entry_builder.hcx {
153             entry.hash_stable(hcx, hasher);
154         }
155
156         let entry = entry_builder.ecx.lazy(&entry);
157         entry_builder.finish(id);
158         self.items.record(id, entry);
159     }
160
161     pub fn into_items(self) -> Index {
162         self.items
163     }
164 }
165
166 /// Trait used for data that can be passed from outside a dep-graph
167 /// task.  The data must either be of some safe type, such as a
168 /// `DefId` index, or implement the `read` method so that it can add
169 /// a read of whatever dep-graph nodes are appropriate.
170 pub trait DepGraphRead {
171     fn read(&self, tcx: TyCtxt);
172 }
173
174 impl DepGraphRead for DefId {
175     fn read(&self, _tcx: TyCtxt) {}
176 }
177
178 impl DepGraphRead for ast::NodeId {
179     fn read(&self, _tcx: TyCtxt) {}
180 }
181
182 impl<T> DepGraphRead for Option<T>
183     where T: DepGraphRead
184 {
185     fn read(&self, tcx: TyCtxt) {
186         match *self {
187             Some(ref v) => v.read(tcx),
188             None => (),
189         }
190     }
191 }
192
193 impl<T> DepGraphRead for [T]
194     where T: DepGraphRead
195 {
196     fn read(&self, tcx: TyCtxt) {
197         for i in self {
198             i.read(tcx);
199         }
200     }
201 }
202
203 macro_rules! read_tuple {
204     ($($name:ident),*) => {
205         impl<$($name),*> DepGraphRead for ($($name),*)
206             where $($name: DepGraphRead),*
207         {
208             #[allow(non_snake_case)]
209             fn read(&self, tcx: TyCtxt) {
210                 let &($(ref $name),*) = self;
211                 $($name.read(tcx);)*
212             }
213         }
214     }
215 }
216 read_tuple!(A, B);
217 read_tuple!(A, B, C);
218
219 macro_rules! read_hir {
220     ($t:ty) => {
221         impl<'tcx> DepGraphRead for &'tcx $t {
222             fn read(&self, tcx: TyCtxt) {
223                 tcx.hir.read(self.id);
224             }
225         }
226     }
227 }
228 read_hir!(hir::Item);
229 read_hir!(hir::ImplItem);
230 read_hir!(hir::TraitItem);
231 read_hir!(hir::ForeignItem);
232 read_hir!(hir::MacroDef);
233
234 /// Leaks access to a value of type T without any tracking. This is
235 /// suitable for ambiguous types like `usize`, which *could* represent
236 /// tracked data (e.g., if you read it out of a HIR node) or might not
237 /// (e.g., if it's an index). Adding in an `Untracked` is an
238 /// assertion, essentially, that the data does not need to be tracked
239 /// (or that read edges will be added by some other way).
240 ///
241 /// A good idea is to add to each use of `Untracked` an explanation of
242 /// why this value is ok.
243 pub struct Untracked<T>(pub T);
244
245 impl<T> DepGraphRead for Untracked<T> {
246     fn read(&self, _tcx: TyCtxt) {}
247 }
248
249 /// Newtype that can be used to package up misc data extracted from a
250 /// HIR node that doesn't carry its own id. This will allow an
251 /// arbitrary `T` to be passed in, but register a read on the given
252 /// node-id.
253 pub struct FromId<T>(pub ast::NodeId, pub T);
254
255 impl<T> DepGraphRead for FromId<T> {
256     fn read(&self, tcx: TyCtxt) {
257         tcx.hir.read(self.0);
258     }
259 }
260
261 pub struct EntryBuilder<'a, 'b: 'a, 'tcx: 'b> {
262     pub tcx: TyCtxt<'b, 'tcx, 'tcx>,
263     ecx: &'a mut EncodeContext<'b, 'tcx>,
264     hcx: Option<(StableHashingContext<'b, 'tcx>, StableHasher<Fingerprint>)>,
265 }
266
267 impl<'a, 'b: 'a, 'tcx: 'b> EntryBuilder<'a, 'b, 'tcx> {
268
269     pub fn finish(self, def_id: DefId) {
270         if let Some((_, hasher)) = self.hcx {
271             let hash = hasher.finish();
272             self.ecx.metadata_hashes.push(EncodedMetadataHash {
273                 def_index: def_id.index,
274                 hash: hash,
275             });
276         }
277     }
278
279     pub fn lazy<T>(&mut self, value: &T) -> Lazy<T>
280         where T: Encodable + HashStable<StableHashingContext<'b, 'tcx>>
281     {
282         if let Some((ref mut hcx, ref mut hasher)) = self.hcx {
283             value.hash_stable(hcx, hasher);
284             debug!("metadata-hash: {:?}", hasher);
285         }
286         self.ecx.lazy(value)
287     }
288
289     pub fn lazy_seq<I, T>(&mut self, iter: I) -> LazySeq<T>
290         where I: IntoIterator<Item = T>,
291               T: Encodable + HashStable<StableHashingContext<'b, 'tcx>>
292     {
293         if let Some((ref mut hcx, ref mut hasher)) = self.hcx {
294             let iter = iter.into_iter();
295             let (lower_bound, upper_bound) = iter.size_hint();
296
297             if upper_bound == Some(lower_bound) {
298                 lower_bound.hash_stable(hcx, hasher);
299                 let mut num_items_hashed = 0;
300                 let ret = self.ecx.lazy_seq(iter.inspect(|item| {
301                     item.hash_stable(hcx, hasher);
302                     num_items_hashed += 1;
303                 }));
304
305                 // Sometimes items in a sequence are filtered out without being
306                 // hashed (e.g. for &[ast::Attribute]) and this code path cannot
307                 // handle that correctly, so we want to make sure we didn't hit
308                 // it by accident.
309                 if lower_bound != num_items_hashed {
310                     bug!("Hashed a different number of items ({}) than expected ({})",
311                          num_items_hashed,
312                          lower_bound);
313                 }
314                 debug!("metadata-hash: {:?}", hasher);
315                 ret
316             } else {
317                 // Collect into a vec so we know the length of the sequence
318                 let items: AccumulateVec<[T; 32]> = iter.collect();
319                 items.hash_stable(hcx, hasher);
320                 debug!("metadata-hash: {:?}", hasher);
321                 self.ecx.lazy_seq(items)
322             }
323         } else {
324             self.ecx.lazy_seq(iter)
325         }
326     }
327
328     pub fn lazy_seq_from_slice<T>(&mut self, slice: &[T]) -> LazySeq<T>
329         where T: Encodable + HashStable<StableHashingContext<'b, 'tcx>>
330     {
331         if let Some((ref mut hcx, ref mut hasher)) = self.hcx {
332             slice.hash_stable(hcx, hasher);
333             debug!("metadata-hash: {:?}", hasher);
334         }
335         self.ecx.lazy_seq_ref(slice.iter())
336     }
337
338     pub fn lazy_seq_ref_from_slice<T>(&mut self, slice: &[&T]) -> LazySeq<T>
339         where T: Encodable + HashStable<StableHashingContext<'b, 'tcx>>
340     {
341         if let Some((ref mut hcx, ref mut hasher)) = self.hcx {
342             slice.hash_stable(hcx, hasher);
343             debug!("metadata-hash: {:?}", hasher);
344         }
345         self.ecx.lazy_seq_ref(slice.iter().map(|x| *x))
346     }
347 }