]> git.proxmox.com Git - rustc.git/blob - src/librustc_incremental/calculate_svh.rs
Imported Upstream version 1.11.0+dfsg1
[rustc.git] / src / librustc_incremental / calculate_svh.rs
1 // Copyright 2012-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 //! Calculation of a Strict Version Hash for crates. For a length
12 //! comment explaining the general idea, see `librustc/middle/svh.rs`.
13
14 use std::hash::{Hash, SipHasher, Hasher};
15 use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId};
16 use rustc::hir::svh::Svh;
17 use rustc::ty::TyCtxt;
18 use rustc::hir::intravisit::{self, Visitor};
19
20 use self::svh_visitor::StrictVersionHashVisitor;
21
22 pub trait SvhCalculate {
23 /// Calculate the SVH for an entire krate.
24 fn calculate_krate_hash(self) -> Svh;
25
26 /// Calculate the SVH for a particular item.
27 fn calculate_item_hash(self, def_id: DefId) -> u64;
28 }
29
30 impl<'a, 'tcx> SvhCalculate for TyCtxt<'a, 'tcx, 'tcx> {
31 fn calculate_krate_hash(self) -> Svh {
32 // FIXME (#14132): This is better than it used to be, but it still not
33 // ideal. We now attempt to hash only the relevant portions of the
34 // Crate AST as well as the top-level crate attributes. (However,
35 // the hashing of the crate attributes should be double-checked
36 // to ensure it is not incorporating implementation artifacts into
37 // the hash that are not otherwise visible.)
38
39 let crate_disambiguator = self.sess.crate_disambiguator.get();
40 let krate = self.map.krate();
41
42 // FIXME: this should use SHA1, not SipHash. SipHash is not built to
43 // avoid collisions.
44 let mut state = SipHasher::new();
45 debug!("state: {:?}", state);
46
47 // FIXME(#32753) -- at (*) we `to_le` for endianness, but is
48 // this enough, and does it matter anyway?
49 "crate_disambiguator".hash(&mut state);
50 crate_disambiguator.as_str().len().to_le().hash(&mut state); // (*)
51 crate_disambiguator.as_str().hash(&mut state);
52
53 debug!("crate_disambiguator: {:?}", crate_disambiguator.as_str());
54 debug!("state: {:?}", state);
55
56 {
57 let mut visit = StrictVersionHashVisitor::new(&mut state, self);
58 krate.visit_all_items(&mut visit);
59 }
60
61 // FIXME (#14132): This hash is still sensitive to e.g. the
62 // spans of the crate Attributes and their underlying
63 // MetaItems; we should make ContentHashable impl for those
64 // types and then use hash_content. But, since all crate
65 // attributes should appear near beginning of the file, it is
66 // not such a big deal to be sensitive to their spans for now.
67 //
68 // We hash only the MetaItems instead of the entire Attribute
69 // to avoid hashing the AttrId
70 for attr in &krate.attrs {
71 debug!("krate attr {:?}", attr);
72 attr.node.value.hash(&mut state);
73 }
74
75 Svh::new(state.finish())
76 }
77
78 fn calculate_item_hash(self, def_id: DefId) -> u64 {
79 assert!(def_id.is_local());
80
81 debug!("calculate_item_hash(def_id={:?})", def_id);
82
83 let mut state = SipHasher::new();
84
85 {
86 let mut visit = StrictVersionHashVisitor::new(&mut state, self);
87 if def_id.index == CRATE_DEF_INDEX {
88 // the crate root itself is not registered in the map
89 // as an item, so we have to fetch it this way
90 let krate = self.map.krate();
91 intravisit::walk_crate(&mut visit, krate);
92 } else {
93 let node_id = self.map.as_local_node_id(def_id).unwrap();
94 let item = self.map.expect_item(node_id);
95 visit.visit_item(item);
96 }
97 }
98
99 let hash = state.finish();
100
101 debug!("calculate_item_hash: def_id={:?} hash={:?}", def_id, hash);
102
103 hash
104 }
105 }
106
107 // FIXME (#14132): Even this SVH computation still has implementation
108 // artifacts: namely, the order of item declaration will affect the
109 // hash computation, but for many kinds of items the order of
110 // declaration should be irrelevant to the ABI.
111
112 mod svh_visitor {
113 pub use self::SawExprComponent::*;
114 pub use self::SawStmtComponent::*;
115 use self::SawAbiComponent::*;
116 use syntax::ast::{self, Name, NodeId};
117 use syntax::parse::token;
118 use syntax_pos::Span;
119 use rustc::ty::TyCtxt;
120 use rustc::hir;
121 use rustc::hir::*;
122 use rustc::hir::intravisit as visit;
123 use rustc::hir::intravisit::{Visitor, FnKind};
124
125 use std::hash::{Hash, SipHasher};
126
127 pub struct StrictVersionHashVisitor<'a, 'tcx: 'a> {
128 pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
129 pub st: &'a mut SipHasher,
130 }
131
132 impl<'a, 'tcx> StrictVersionHashVisitor<'a, 'tcx> {
133 pub fn new(st: &'a mut SipHasher,
134 tcx: TyCtxt<'a, 'tcx, 'tcx>)
135 -> Self {
136 StrictVersionHashVisitor { st: st, tcx: tcx }
137 }
138 }
139
140 // To off-load the bulk of the hash-computation on #[derive(Hash)],
141 // we define a set of enums corresponding to the content that our
142 // crate visitor will encounter as it traverses the ast.
143 //
144 // The important invariant is that all of the Saw*Component enums
145 // do not carry any Spans, Names, or Idents.
146 //
147 // Not carrying any Names/Idents is the important fix for problem
148 // noted on PR #13948: using the ident.name as the basis for a
149 // hash leads to unstable SVH, because ident.name is just an index
150 // into intern table (i.e. essentially a random address), not
151 // computed from the name content.
152 //
153 // With the below enums, the SVH computation is not sensitive to
154 // artifacts of how rustc was invoked nor of how the source code
155 // was laid out. (Or at least it is *less* sensitive.)
156
157 // This enum represents the different potential bits of code the
158 // visitor could encounter that could affect the ABI for the crate,
159 // and assigns each a distinct tag to feed into the hash computation.
160 #[derive(Hash)]
161 enum SawAbiComponent<'a> {
162
163 // FIXME (#14132): should we include (some function of)
164 // ident.ctxt as well?
165 SawIdent(token::InternedString),
166 SawStructDef(token::InternedString),
167
168 SawLifetime(token::InternedString),
169 SawLifetimeDef(token::InternedString),
170
171 SawMod,
172 SawForeignItem,
173 SawItem,
174 SawDecl,
175 SawTy,
176 SawGenerics,
177 SawFn,
178 SawTraitItem,
179 SawImplItem,
180 SawStructField,
181 SawVariant,
182 SawPath,
183 SawBlock,
184 SawPat,
185 SawLocal,
186 SawArm,
187 SawExpr(SawExprComponent<'a>),
188 SawStmt(SawStmtComponent),
189 }
190
191 /// SawExprComponent carries all of the information that we want
192 /// to include in the hash that *won't* be covered by the
193 /// subsequent recursive traversal of the expression's
194 /// substructure by the visitor.
195 ///
196 /// We know every Expr_ variant is covered by a variant because
197 /// `fn saw_expr` maps each to some case below. Ensuring that
198 /// each variant carries an appropriate payload has to be verified
199 /// by hand.
200 ///
201 /// (However, getting that *exactly* right is not so important
202 /// because the SVH is just a developer convenience; there is no
203 /// guarantee of collision-freedom, hash collisions are just
204 /// (hopefully) unlikely.)
205 #[derive(Hash)]
206 pub enum SawExprComponent<'a> {
207
208 SawExprLoop(Option<token::InternedString>),
209 SawExprField(token::InternedString),
210 SawExprTupField(usize),
211 SawExprBreak(Option<token::InternedString>),
212 SawExprAgain(Option<token::InternedString>),
213
214 SawExprBox,
215 SawExprVec,
216 SawExprCall,
217 SawExprMethodCall,
218 SawExprTup,
219 SawExprBinary(hir::BinOp_),
220 SawExprUnary(hir::UnOp),
221 SawExprLit(ast::LitKind),
222 SawExprCast,
223 SawExprType,
224 SawExprIf,
225 SawExprWhile,
226 SawExprMatch,
227 SawExprClosure,
228 SawExprBlock,
229 SawExprAssign,
230 SawExprAssignOp(hir::BinOp_),
231 SawExprIndex,
232 SawExprPath(Option<usize>),
233 SawExprAddrOf(hir::Mutability),
234 SawExprRet,
235 SawExprInlineAsm(&'a hir::InlineAsm),
236 SawExprStruct,
237 SawExprRepeat,
238 }
239
240 fn saw_expr<'a>(node: &'a Expr_) -> SawExprComponent<'a> {
241 match *node {
242 ExprBox(..) => SawExprBox,
243 ExprVec(..) => SawExprVec,
244 ExprCall(..) => SawExprCall,
245 ExprMethodCall(..) => SawExprMethodCall,
246 ExprTup(..) => SawExprTup,
247 ExprBinary(op, _, _) => SawExprBinary(op.node),
248 ExprUnary(op, _) => SawExprUnary(op),
249 ExprLit(ref lit) => SawExprLit(lit.node.clone()),
250 ExprCast(..) => SawExprCast,
251 ExprType(..) => SawExprType,
252 ExprIf(..) => SawExprIf,
253 ExprWhile(..) => SawExprWhile,
254 ExprLoop(_, id) => SawExprLoop(id.map(|id| id.node.as_str())),
255 ExprMatch(..) => SawExprMatch,
256 ExprClosure(..) => SawExprClosure,
257 ExprBlock(..) => SawExprBlock,
258 ExprAssign(..) => SawExprAssign,
259 ExprAssignOp(op, _, _) => SawExprAssignOp(op.node),
260 ExprField(_, name) => SawExprField(name.node.as_str()),
261 ExprTupField(_, id) => SawExprTupField(id.node),
262 ExprIndex(..) => SawExprIndex,
263 ExprPath(ref qself, _) => SawExprPath(qself.as_ref().map(|q| q.position)),
264 ExprAddrOf(m, _) => SawExprAddrOf(m),
265 ExprBreak(id) => SawExprBreak(id.map(|id| id.node.as_str())),
266 ExprAgain(id) => SawExprAgain(id.map(|id| id.node.as_str())),
267 ExprRet(..) => SawExprRet,
268 ExprInlineAsm(ref a,_,_) => SawExprInlineAsm(a),
269 ExprStruct(..) => SawExprStruct,
270 ExprRepeat(..) => SawExprRepeat,
271 }
272 }
273
274 /// SawStmtComponent is analogous to SawExprComponent, but for statements.
275 #[derive(Hash)]
276 pub enum SawStmtComponent {
277 SawStmtDecl,
278 SawStmtExpr,
279 SawStmtSemi,
280 }
281
282 fn saw_stmt(node: &Stmt_) -> SawStmtComponent {
283 match *node {
284 StmtDecl(..) => SawStmtDecl,
285 StmtExpr(..) => SawStmtExpr,
286 StmtSemi(..) => SawStmtSemi,
287 }
288 }
289
290 impl<'a, 'tcx> Visitor<'a> for StrictVersionHashVisitor<'a, 'tcx> {
291 fn visit_nested_item(&mut self, item: ItemId) {
292 debug!("visit_nested_item: {:?} st={:?}", item, self.st);
293 let def_path = self.tcx.map.def_path_from_id(item.id);
294 def_path.hash(self.st);
295 }
296
297 fn visit_variant_data(&mut self, s: &'a VariantData, name: Name,
298 g: &'a Generics, _: NodeId, _: Span) {
299 SawStructDef(name.as_str()).hash(self.st);
300 visit::walk_generics(self, g);
301 visit::walk_struct_def(self, s)
302 }
303
304 fn visit_variant(&mut self, v: &'a Variant, g: &'a Generics, item_id: NodeId) {
305 SawVariant.hash(self.st);
306 // walk_variant does not call walk_generics, so do it here.
307 visit::walk_generics(self, g);
308 visit::walk_variant(self, v, g, item_id)
309 }
310
311 // All of the remaining methods just record (in the hash
312 // SipHasher) that the visitor saw that particular variant
313 // (with its payload), and continue walking as the default
314 // visitor would.
315 //
316 // Some of the implementations have some notes as to how one
317 // might try to make their SVH computation less discerning
318 // (e.g. by incorporating reachability analysis). But
319 // currently all of their implementations are uniform and
320 // uninteresting.
321 //
322 // (If you edit a method such that it deviates from the
323 // pattern, please move that method up above this comment.)
324
325 fn visit_name(&mut self, _: Span, name: Name) {
326 SawIdent(name.as_str()).hash(self.st);
327 }
328
329 fn visit_lifetime(&mut self, l: &'a Lifetime) {
330 SawLifetime(l.name.as_str()).hash(self.st);
331 }
332
333 fn visit_lifetime_def(&mut self, l: &'a LifetimeDef) {
334 SawLifetimeDef(l.lifetime.name.as_str()).hash(self.st);
335 }
336
337 // We do recursively walk the bodies of functions/methods
338 // (rather than omitting their bodies from the hash) since
339 // monomorphization and cross-crate inlining generally implies
340 // that a change to a crate body will require downstream
341 // crates to be recompiled.
342 fn visit_expr(&mut self, ex: &'a Expr) {
343 SawExpr(saw_expr(&ex.node)).hash(self.st); visit::walk_expr(self, ex)
344 }
345
346 fn visit_stmt(&mut self, s: &'a Stmt) {
347 SawStmt(saw_stmt(&s.node)).hash(self.st); visit::walk_stmt(self, s)
348 }
349
350 fn visit_foreign_item(&mut self, i: &'a ForeignItem) {
351 // FIXME (#14132) ideally we would incorporate privacy (or
352 // perhaps reachability) somewhere here, so foreign items
353 // that do not leak into downstream crates would not be
354 // part of the ABI.
355 SawForeignItem.hash(self.st); visit::walk_foreign_item(self, i)
356 }
357
358 fn visit_item(&mut self, i: &'a Item) {
359 debug!("visit_item: {:?} st={:?}", i, self.st);
360 // FIXME (#14132) ideally would incorporate reachability
361 // analysis somewhere here, so items that never leak into
362 // downstream crates (e.g. via monomorphisation or
363 // inlining) would not be part of the ABI.
364 SawItem.hash(self.st); visit::walk_item(self, i)
365 }
366
367 fn visit_mod(&mut self, m: &'a Mod, _s: Span, _n: NodeId) {
368 SawMod.hash(self.st); visit::walk_mod(self, m)
369 }
370
371 fn visit_decl(&mut self, d: &'a Decl) {
372 SawDecl.hash(self.st); visit::walk_decl(self, d)
373 }
374
375 fn visit_ty(&mut self, t: &'a Ty) {
376 SawTy.hash(self.st); visit::walk_ty(self, t)
377 }
378
379 fn visit_generics(&mut self, g: &'a Generics) {
380 SawGenerics.hash(self.st); visit::walk_generics(self, g)
381 }
382
383 fn visit_fn(&mut self, fk: FnKind<'a>, fd: &'a FnDecl,
384 b: &'a Block, s: Span, _: NodeId) {
385 SawFn.hash(self.st); visit::walk_fn(self, fk, fd, b, s)
386 }
387
388 fn visit_trait_item(&mut self, ti: &'a TraitItem) {
389 SawTraitItem.hash(self.st); visit::walk_trait_item(self, ti)
390 }
391
392 fn visit_impl_item(&mut self, ii: &'a ImplItem) {
393 SawImplItem.hash(self.st); visit::walk_impl_item(self, ii)
394 }
395
396 fn visit_struct_field(&mut self, s: &'a StructField) {
397 SawStructField.hash(self.st); visit::walk_struct_field(self, s)
398 }
399
400 fn visit_path(&mut self, path: &'a Path, _: ast::NodeId) {
401 SawPath.hash(self.st); visit::walk_path(self, path)
402 }
403
404 fn visit_block(&mut self, b: &'a Block) {
405 SawBlock.hash(self.st); visit::walk_block(self, b)
406 }
407
408 fn visit_pat(&mut self, p: &'a Pat) {
409 SawPat.hash(self.st); visit::walk_pat(self, p)
410 }
411
412 fn visit_local(&mut self, l: &'a Local) {
413 SawLocal.hash(self.st); visit::walk_local(self, l)
414 }
415
416 fn visit_arm(&mut self, a: &'a Arm) {
417 SawArm.hash(self.st); visit::walk_arm(self, a)
418 }
419 }
420 }