]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
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 | ||
54a0048b SL |
11 | //! Calculation of a Strict Version Hash for crates. For a length |
12 | //! comment explaining the general idea, see `librustc/middle/svh.rs`. | |
13 | ||
1a4d82fc | 14 | use std::hash::{Hash, SipHasher, Hasher}; |
54a0048b SL |
15 | use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId}; |
16 | use rustc::hir::svh::Svh; | |
a7813a04 | 17 | use rustc::ty::TyCtxt; |
54a0048b | 18 | use rustc::hir::intravisit::{self, Visitor}; |
1a4d82fc | 19 | |
54a0048b | 20 | use self::svh_visitor::StrictVersionHashVisitor; |
1a4d82fc | 21 | |
54a0048b SL |
22 | pub trait SvhCalculate { |
23 | /// Calculate the SVH for an entire krate. | |
a7813a04 | 24 | fn calculate_krate_hash(self) -> Svh; |
1a4d82fc | 25 | |
54a0048b | 26 | /// Calculate the SVH for a particular item. |
a7813a04 | 27 | fn calculate_item_hash(self, def_id: DefId) -> u64; |
54a0048b | 28 | } |
1a4d82fc | 29 | |
a7813a04 XL |
30 | impl<'a, 'tcx> SvhCalculate for TyCtxt<'a, 'tcx, 'tcx> { |
31 | fn calculate_krate_hash(self) -> Svh { | |
1a4d82fc JJ |
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 | ||
54a0048b SL |
39 | let crate_disambiguator = self.sess.crate_disambiguator.get(); |
40 | let krate = self.map.krate(); | |
41 | ||
1a4d82fc JJ |
42 | // FIXME: this should use SHA1, not SipHash. SipHash is not built to |
43 | // avoid collisions. | |
44 | let mut state = SipHasher::new(); | |
54a0048b | 45 | debug!("state: {:?}", state); |
1a4d82fc | 46 | |
54a0048b SL |
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); | |
1a4d82fc JJ |
55 | |
56 | { | |
54a0048b SL |
57 | let mut visit = StrictVersionHashVisitor::new(&mut state, self); |
58 | krate.visit_all_items(&mut visit); | |
1a4d82fc JJ |
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 | |
85aaf69f | 70 | for attr in &krate.attrs { |
54a0048b | 71 | debug!("krate attr {:?}", attr); |
1a4d82fc JJ |
72 | attr.node.value.hash(&mut state); |
73 | } | |
74 | ||
a7813a04 | 75 | Svh::new(state.finish()) |
1a4d82fc | 76 | } |
1a4d82fc | 77 | |
a7813a04 | 78 | fn calculate_item_hash(self, def_id: DefId) -> u64 { |
54a0048b SL |
79 | assert!(def_id.is_local()); |
80 | ||
a7813a04 XL |
81 | debug!("calculate_item_hash(def_id={:?})", def_id); |
82 | ||
54a0048b SL |
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(); | |
a7813a04 XL |
94 | let item = self.map.expect_item(node_id); |
95 | visit.visit_item(item); | |
54a0048b SL |
96 | } |
97 | } | |
98 | ||
a7813a04 XL |
99 | let hash = state.finish(); |
100 | ||
101 | debug!("calculate_item_hash: def_id={:?} hash={:?}", def_id, hash); | |
102 | ||
103 | hash | |
1a4d82fc JJ |
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::*; | |
b039eaaf | 116 | use syntax::ast::{self, Name, NodeId}; |
1a4d82fc | 117 | use syntax::parse::token; |
3157f602 | 118 | use syntax_pos::Span; |
a7813a04 | 119 | use rustc::ty::TyCtxt; |
54a0048b SL |
120 | use rustc::hir; |
121 | use rustc::hir::*; | |
122 | use rustc::hir::intravisit as visit; | |
123 | use rustc::hir::intravisit::{Visitor, FnKind}; | |
1a4d82fc JJ |
124 | |
125 | use std::hash::{Hash, SipHasher}; | |
126 | ||
54a0048b | 127 | pub struct StrictVersionHashVisitor<'a, 'tcx: 'a> { |
a7813a04 | 128 | pub tcx: TyCtxt<'a, 'tcx, 'tcx>, |
1a4d82fc JJ |
129 | pub st: &'a mut SipHasher, |
130 | } | |
131 | ||
54a0048b SL |
132 | impl<'a, 'tcx> StrictVersionHashVisitor<'a, 'tcx> { |
133 | pub fn new(st: &'a mut SipHasher, | |
a7813a04 | 134 | tcx: TyCtxt<'a, 'tcx, 'tcx>) |
54a0048b SL |
135 | -> Self { |
136 | StrictVersionHashVisitor { st: st, tcx: tcx } | |
137 | } | |
1a4d82fc JJ |
138 | } |
139 | ||
85aaf69f | 140 | // To off-load the bulk of the hash-computation on #[derive(Hash)], |
1a4d82fc JJ |
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 | ||
b039eaaf | 168 | SawLifetime(token::InternedString), |
1a4d82fc JJ |
169 | SawLifetimeDef(token::InternedString), |
170 | ||
171 | SawMod, | |
1a4d82fc JJ |
172 | SawForeignItem, |
173 | SawItem, | |
174 | SawDecl, | |
175 | SawTy, | |
176 | SawGenerics, | |
177 | SawFn, | |
c34b1796 AL |
178 | SawTraitItem, |
179 | SawImplItem, | |
1a4d82fc JJ |
180 | SawStructField, |
181 | SawVariant, | |
1a4d82fc | 182 | SawPath, |
1a4d82fc JJ |
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), | |
c34b1796 | 210 | SawExprTupField(usize), |
1a4d82fc JJ |
211 | SawExprBreak(Option<token::InternedString>), |
212 | SawExprAgain(Option<token::InternedString>), | |
213 | ||
214 | SawExprBox, | |
215 | SawExprVec, | |
216 | SawExprCall, | |
217 | SawExprMethodCall, | |
218 | SawExprTup, | |
e9174d1e SL |
219 | SawExprBinary(hir::BinOp_), |
220 | SawExprUnary(hir::UnOp), | |
7453a54e | 221 | SawExprLit(ast::LitKind), |
1a4d82fc | 222 | SawExprCast, |
9cc50fc6 | 223 | SawExprType, |
1a4d82fc JJ |
224 | SawExprIf, |
225 | SawExprWhile, | |
226 | SawExprMatch, | |
227 | SawExprClosure, | |
228 | SawExprBlock, | |
229 | SawExprAssign, | |
e9174d1e | 230 | SawExprAssignOp(hir::BinOp_), |
1a4d82fc | 231 | SawExprIndex, |
c34b1796 | 232 | SawExprPath(Option<usize>), |
e9174d1e | 233 | SawExprAddrOf(hir::Mutability), |
1a4d82fc | 234 | SawExprRet, |
e9174d1e | 235 | SawExprInlineAsm(&'a hir::InlineAsm), |
1a4d82fc JJ |
236 | SawExprStruct, |
237 | SawExprRepeat, | |
1a4d82fc JJ |
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, | |
85aaf69f | 247 | ExprBinary(op, _, _) => SawExprBinary(op.node), |
1a4d82fc JJ |
248 | ExprUnary(op, _) => SawExprUnary(op), |
249 | ExprLit(ref lit) => SawExprLit(lit.node.clone()), | |
250 | ExprCast(..) => SawExprCast, | |
9cc50fc6 | 251 | ExprType(..) => SawExprType, |
1a4d82fc JJ |
252 | ExprIf(..) => SawExprIf, |
253 | ExprWhile(..) => SawExprWhile, | |
3157f602 | 254 | ExprLoop(_, id) => SawExprLoop(id.map(|id| id.node.as_str())), |
1a4d82fc JJ |
255 | ExprMatch(..) => SawExprMatch, |
256 | ExprClosure(..) => SawExprClosure, | |
257 | ExprBlock(..) => SawExprBlock, | |
258 | ExprAssign(..) => SawExprAssign, | |
85aaf69f | 259 | ExprAssignOp(op, _, _) => SawExprAssignOp(op.node), |
b039eaaf | 260 | ExprField(_, name) => SawExprField(name.node.as_str()), |
1a4d82fc JJ |
261 | ExprTupField(_, id) => SawExprTupField(id.node), |
262 | ExprIndex(..) => SawExprIndex, | |
c34b1796 | 263 | ExprPath(ref qself, _) => SawExprPath(qself.as_ref().map(|q| q.position)), |
1a4d82fc | 264 | ExprAddrOf(m, _) => SawExprAddrOf(m), |
a7813a04 XL |
265 | ExprBreak(id) => SawExprBreak(id.map(|id| id.node.as_str())), |
266 | ExprAgain(id) => SawExprAgain(id.map(|id| id.node.as_str())), | |
1a4d82fc | 267 | ExprRet(..) => SawExprRet, |
54a0048b | 268 | ExprInlineAsm(ref a,_,_) => SawExprInlineAsm(a), |
1a4d82fc JJ |
269 | ExprStruct(..) => SawExprStruct, |
270 | ExprRepeat(..) => SawExprRepeat, | |
1a4d82fc JJ |
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, | |
1a4d82fc JJ |
287 | } |
288 | } | |
289 | ||
54a0048b | 290 | impl<'a, 'tcx> Visitor<'a> for StrictVersionHashVisitor<'a, 'tcx> { |
92a42be0 | 291 | fn visit_nested_item(&mut self, item: ItemId) { |
54a0048b SL |
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); | |
92a42be0 SL |
295 | } |
296 | ||
297 | fn visit_variant_data(&mut self, s: &'a VariantData, name: Name, | |
298 | g: &'a Generics, _: NodeId, _: Span) { | |
b039eaaf | 299 | SawStructDef(name.as_str()).hash(self.st); |
1a4d82fc JJ |
300 | visit::walk_generics(self, g); |
301 | visit::walk_struct_def(self, s) | |
302 | } | |
303 | ||
92a42be0 | 304 | fn visit_variant(&mut self, v: &'a Variant, g: &'a Generics, item_id: NodeId) { |
1a4d82fc JJ |
305 | SawVariant.hash(self.st); |
306 | // walk_variant does not call walk_generics, so do it here. | |
307 | visit::walk_generics(self, g); | |
b039eaaf | 308 | visit::walk_variant(self, v, g, item_id) |
1a4d82fc JJ |
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 | ||
b039eaaf SL |
325 | fn visit_name(&mut self, _: Span, name: Name) { |
326 | SawIdent(name.as_str()).hash(self.st); | |
1a4d82fc JJ |
327 | } |
328 | ||
92a42be0 | 329 | fn visit_lifetime(&mut self, l: &'a Lifetime) { |
b039eaaf | 330 | SawLifetime(l.name.as_str()).hash(self.st); |
1a4d82fc JJ |
331 | } |
332 | ||
92a42be0 | 333 | fn visit_lifetime_def(&mut self, l: &'a LifetimeDef) { |
c1a9b12d | 334 | SawLifetimeDef(l.lifetime.name.as_str()).hash(self.st); |
1a4d82fc JJ |
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. | |
92a42be0 | 342 | fn visit_expr(&mut self, ex: &'a Expr) { |
1a4d82fc JJ |
343 | SawExpr(saw_expr(&ex.node)).hash(self.st); visit::walk_expr(self, ex) |
344 | } | |
345 | ||
92a42be0 | 346 | fn visit_stmt(&mut self, s: &'a Stmt) { |
1a4d82fc JJ |
347 | SawStmt(saw_stmt(&s.node)).hash(self.st); visit::walk_stmt(self, s) |
348 | } | |
349 | ||
92a42be0 | 350 | fn visit_foreign_item(&mut self, i: &'a ForeignItem) { |
1a4d82fc JJ |
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 | ||
92a42be0 | 358 | fn visit_item(&mut self, i: &'a Item) { |
54a0048b | 359 | debug!("visit_item: {:?} st={:?}", i, self.st); |
1a4d82fc JJ |
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 | ||
92a42be0 | 367 | fn visit_mod(&mut self, m: &'a Mod, _s: Span, _n: NodeId) { |
1a4d82fc JJ |
368 | SawMod.hash(self.st); visit::walk_mod(self, m) |
369 | } | |
370 | ||
92a42be0 | 371 | fn visit_decl(&mut self, d: &'a Decl) { |
1a4d82fc JJ |
372 | SawDecl.hash(self.st); visit::walk_decl(self, d) |
373 | } | |
374 | ||
92a42be0 | 375 | fn visit_ty(&mut self, t: &'a Ty) { |
1a4d82fc JJ |
376 | SawTy.hash(self.st); visit::walk_ty(self, t) |
377 | } | |
378 | ||
92a42be0 | 379 | fn visit_generics(&mut self, g: &'a Generics) { |
1a4d82fc JJ |
380 | SawGenerics.hash(self.st); visit::walk_generics(self, g) |
381 | } | |
382 | ||
92a42be0 SL |
383 | fn visit_fn(&mut self, fk: FnKind<'a>, fd: &'a FnDecl, |
384 | b: &'a Block, s: Span, _: NodeId) { | |
1a4d82fc JJ |
385 | SawFn.hash(self.st); visit::walk_fn(self, fk, fd, b, s) |
386 | } | |
387 | ||
92a42be0 | 388 | fn visit_trait_item(&mut self, ti: &'a TraitItem) { |
c34b1796 | 389 | SawTraitItem.hash(self.st); visit::walk_trait_item(self, ti) |
1a4d82fc JJ |
390 | } |
391 | ||
92a42be0 | 392 | fn visit_impl_item(&mut self, ii: &'a ImplItem) { |
c34b1796 | 393 | SawImplItem.hash(self.st); visit::walk_impl_item(self, ii) |
1a4d82fc JJ |
394 | } |
395 | ||
92a42be0 | 396 | fn visit_struct_field(&mut self, s: &'a StructField) { |
1a4d82fc JJ |
397 | SawStructField.hash(self.st); visit::walk_struct_field(self, s) |
398 | } | |
399 | ||
92a42be0 | 400 | fn visit_path(&mut self, path: &'a Path, _: ast::NodeId) { |
1a4d82fc JJ |
401 | SawPath.hash(self.st); visit::walk_path(self, path) |
402 | } | |
403 | ||
92a42be0 | 404 | fn visit_block(&mut self, b: &'a Block) { |
1a4d82fc JJ |
405 | SawBlock.hash(self.st); visit::walk_block(self, b) |
406 | } | |
407 | ||
92a42be0 | 408 | fn visit_pat(&mut self, p: &'a Pat) { |
1a4d82fc JJ |
409 | SawPat.hash(self.st); visit::walk_pat(self, p) |
410 | } | |
411 | ||
92a42be0 | 412 | fn visit_local(&mut self, l: &'a Local) { |
1a4d82fc JJ |
413 | SawLocal.hash(self.st); visit::walk_local(self, l) |
414 | } | |
415 | ||
92a42be0 | 416 | fn visit_arm(&mut self, a: &'a Arm) { |
1a4d82fc JJ |
417 | SawArm.hash(self.st); visit::walk_arm(self, a) |
418 | } | |
419 | } | |
420 | } |