]> git.lizzy.rs Git - rust.git/blob - src/librustc_metadata/index_builder.rs
Auto merge of #56157 - RalfJung:park, r=nagisa
[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 use isolated_encoder::IsolatedEncoder;
62
63 use rustc::hir;
64 use rustc::hir::def_id::DefId;
65 use rustc::ty::TyCtxt;
66 use syntax::ast;
67
68 use std::ops::{Deref, DerefMut};
69
70 /// Builder that can encode new items, adding them into the index.
71 /// Item encoding cannot be nested.
72 pub struct IndexBuilder<'a, 'b: 'a, 'tcx: 'b> {
73     items: Index,
74     pub ecx: &'a mut EncodeContext<'b, 'tcx>,
75 }
76
77 impl<'a, 'b, 'tcx> Deref for IndexBuilder<'a, 'b, 'tcx> {
78     type Target = EncodeContext<'b, 'tcx>;
79     fn deref(&self) -> &Self::Target {
80         self.ecx
81     }
82 }
83
84 impl<'a, 'b, 'tcx> DerefMut for IndexBuilder<'a, 'b, 'tcx> {
85     fn deref_mut(&mut self) -> &mut Self::Target {
86         self.ecx
87     }
88 }
89
90 impl<'a, 'b, 'tcx> IndexBuilder<'a, 'b, 'tcx> {
91     pub fn new(ecx: &'a mut EncodeContext<'b, 'tcx>) -> Self {
92         IndexBuilder {
93             items: Index::new(ecx.tcx.hir().definitions().def_index_counts_lo_hi()),
94             ecx,
95         }
96     }
97
98     /// Emit the data for a def-id to the metadata. The function to
99     /// emit the data is `op`, and it will be given `data` as
100     /// arguments. This `record` function will call `op` to generate
101     /// the `Entry` (which may point to other encoded information)
102     /// and will then record the `Lazy<Entry>` for use in the index.
103     ///
104     /// In addition, it will setup a dep-graph task to track what data
105     /// `op` accesses to generate the metadata, which is later used by
106     /// incremental compilation to compute a hash for the metadata and
107     /// track changes.
108     ///
109     /// The reason that `op` is a function pointer, and not a closure,
110     /// is that we want to be able to completely track all data it has
111     /// access to, so that we can be sure that `DATA: DepGraphRead`
112     /// holds, and that it is therefore not gaining "secret" access to
113     /// bits of HIR or other state that would not be trackd by the
114     /// content system.
115     pub fn record<'x, DATA>(&'x mut self,
116                             id: DefId,
117                             op: fn(&mut IsolatedEncoder<'x, 'b, 'tcx>, DATA) -> Entry<'tcx>,
118                             data: DATA)
119         where DATA: DepGraphRead
120     {
121         assert!(id.is_local());
122
123         // We don't track this since we are explicitly computing the incr. comp.
124         // hashes anyway. In theory we could do some tracking here and use it to
125         // avoid rehashing things (and instead cache the hashes) but it's
126         // unclear whether that would be a win since hashing is cheap enough.
127         self.ecx.tcx.dep_graph.with_ignore(move || {
128             let mut entry_builder = IsolatedEncoder::new(self.ecx);
129             let entry = op(&mut entry_builder, data);
130             let entry = entry_builder.lazy(&entry);
131
132             self.items.record(id, entry);
133         })
134     }
135
136     pub fn into_items(self) -> Index {
137         self.items
138     }
139 }
140
141 /// Trait used for data that can be passed from outside a dep-graph
142 /// task.  The data must either be of some safe type, such as a
143 /// `DefId` index, or implement the `read` method so that it can add
144 /// a read of whatever dep-graph nodes are appropriate.
145 pub trait DepGraphRead {
146     fn read(&self, tcx: TyCtxt);
147 }
148
149 impl DepGraphRead for DefId {
150     fn read(&self, _tcx: TyCtxt) {}
151 }
152
153 impl DepGraphRead for ast::NodeId {
154     fn read(&self, _tcx: TyCtxt) {}
155 }
156
157 impl<T> DepGraphRead for Option<T>
158     where T: DepGraphRead
159 {
160     fn read(&self, tcx: TyCtxt) {
161         match *self {
162             Some(ref v) => v.read(tcx),
163             None => (),
164         }
165     }
166 }
167
168 impl<T> DepGraphRead for [T]
169     where T: DepGraphRead
170 {
171     fn read(&self, tcx: TyCtxt) {
172         for i in self {
173             i.read(tcx);
174         }
175     }
176 }
177
178 macro_rules! read_tuple {
179     ($($name:ident),*) => {
180         impl<$($name),*> DepGraphRead for ($($name),*)
181             where $($name: DepGraphRead),*
182         {
183             #[allow(non_snake_case)]
184             fn read(&self, tcx: TyCtxt) {
185                 let &($(ref $name),*) = self;
186                 $($name.read(tcx);)*
187             }
188         }
189     }
190 }
191 read_tuple!(A, B);
192 read_tuple!(A, B, C);
193
194 macro_rules! read_hir {
195     ($t:ty) => {
196         impl<'tcx> DepGraphRead for &'tcx $t {
197             fn read(&self, tcx: TyCtxt) {
198                 tcx.hir().read(self.id);
199             }
200         }
201     }
202 }
203 read_hir!(hir::Item);
204 read_hir!(hir::ImplItem);
205 read_hir!(hir::TraitItem);
206 read_hir!(hir::ForeignItem);
207 read_hir!(hir::MacroDef);
208
209 /// Leaks access to a value of type T without any tracking. This is
210 /// suitable for ambiguous types like `usize`, which *could* represent
211 /// tracked data (e.g., if you read it out of a HIR node) or might not
212 /// (e.g., if it's an index). Adding in an `Untracked` is an
213 /// assertion, essentially, that the data does not need to be tracked
214 /// (or that read edges will be added by some other way).
215 ///
216 /// A good idea is to add to each use of `Untracked` an explanation of
217 /// why this value is ok.
218 pub struct Untracked<T>(pub T);
219
220 impl<T> DepGraphRead for Untracked<T> {
221     fn read(&self, _tcx: TyCtxt) {}
222 }
223
224 /// Newtype that can be used to package up misc data extracted from a
225 /// HIR node that doesn't carry its own id. This will allow an
226 /// arbitrary `T` to be passed in, but register a read on the given
227 /// node-id.
228 pub struct FromId<T>(pub ast::NodeId, pub T);
229
230 impl<T> DepGraphRead for FromId<T> {
231     fn read(&self, tcx: TyCtxt) {
232         tcx.hir().read(self.0);
233     }
234 }