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