Skip to content

Commit 27f41b7

Browse files
author
Jonathan Turner
authored
Rollup merge of rust-lang#37513 - michaelwoerister:hash-panic-spans, r=nikomatsakis
ICH: Hash expression spans if their source location is captured for panics. Since the location of some expressions is captured in error message constants, it has an influence on machine code and consequently we need to take them into account by the incr. comp. hash. This PR makes this happen for `+, -, *, /, %` and for array indexing -- let me know if I forgot anything. In the future we might want to change the codegen strategy for those error messages, so that they are stored in a separate object file with a stable symbol name, so that only this object file has to be regenerated when source locations change. This strategy would also eliminate unnecessary duplications due to monomorphization, as @arielb1 has pointed out on IRC. I opened rust-lang#37512, so we don't forget about this. r? @nikomatsakis
2 parents 62e026b + 0e391bf commit 27f41b7

File tree

3 files changed

+527
-35
lines changed

3 files changed

+527
-35
lines changed

src/librustc_incremental/calculate_svh/svh_visitor.rs

+103-35
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use self::SawTyComponent::*;
2121
use self::SawTraitOrImplItemComponent::*;
2222
use syntax::abi::Abi;
2323
use syntax::ast::{self, Name, NodeId};
24+
use syntax::attr;
2425
use syntax::parse::token;
2526
use syntax_pos::{Span, NO_EXPANSION, COMMAND_LINE_EXPN, BytePos};
2627
use rustc::hir;
@@ -53,6 +54,7 @@ pub struct StrictVersionHashVisitor<'a, 'hash: 'a, 'tcx: 'hash> {
5354
def_path_hashes: &'a mut DefPathHashes<'hash, 'tcx>,
5455
hash_spans: bool,
5556
codemap: &'a mut CachingCodemapView<'tcx>,
57+
overflow_checks_enabled: bool,
5658
}
5759

5860
impl<'a, 'hash, 'tcx> StrictVersionHashVisitor<'a, 'hash, 'tcx> {
@@ -62,12 +64,16 @@ impl<'a, 'hash, 'tcx> StrictVersionHashVisitor<'a, 'hash, 'tcx> {
6264
codemap: &'a mut CachingCodemapView<'tcx>,
6365
hash_spans: bool)
6466
-> Self {
67+
let check_overflow = tcx.sess.opts.debugging_opts.force_overflow_checks
68+
.unwrap_or(tcx.sess.opts.debug_assertions);
69+
6570
StrictVersionHashVisitor {
6671
st: st,
6772
tcx: tcx,
6873
def_path_hashes: def_path_hashes,
6974
hash_spans: hash_spans,
7075
codemap: codemap,
76+
overflow_checks_enabled: check_overflow,
7177
}
7278
}
7379

@@ -83,7 +89,6 @@ impl<'a, 'hash, 'tcx> StrictVersionHashVisitor<'a, 'hash, 'tcx> {
8389
// Also note that we are hashing byte offsets for the column, not unicode
8490
// codepoint offsets. For the purpose of the hash that's sufficient.
8591
fn hash_span(&mut self, span: Span) {
86-
debug_assert!(self.hash_spans);
8792
debug!("hash_span: st={:?}", self.st);
8893

8994
// If this is not an empty or invalid span, we want to hash the last
@@ -241,37 +246,80 @@ enum SawExprComponent<'a> {
241246
SawExprRepeat,
242247
}
243248

244-
fn saw_expr<'a>(node: &'a Expr_) -> SawExprComponent<'a> {
249+
// The boolean returned indicates whether the span of this expression is always
250+
// significant, regardless of debuginfo.
251+
fn saw_expr<'a>(node: &'a Expr_,
252+
overflow_checks_enabled: bool)
253+
-> (SawExprComponent<'a>, bool) {
254+
let binop_can_panic_at_runtime = |binop| {
255+
match binop {
256+
BiAdd |
257+
BiSub |
258+
BiMul => overflow_checks_enabled,
259+
260+
BiDiv |
261+
BiRem => true,
262+
263+
BiAnd |
264+
BiOr |
265+
BiBitXor |
266+
BiBitAnd |
267+
BiBitOr |
268+
BiShl |
269+
BiShr |
270+
BiEq |
271+
BiLt |
272+
BiLe |
273+
BiNe |
274+
BiGe |
275+
BiGt => false
276+
}
277+
};
278+
279+
let unop_can_panic_at_runtime = |unop| {
280+
match unop {
281+
UnDeref |
282+
UnNot => false,
283+
UnNeg => overflow_checks_enabled,
284+
}
285+
};
286+
245287
match *node {
246-
ExprBox(..) => SawExprBox,
247-
ExprArray(..) => SawExprArray,
248-
ExprCall(..) => SawExprCall,
249-
ExprMethodCall(..) => SawExprMethodCall,
250-
ExprTup(..) => SawExprTup,
251-
ExprBinary(op, ..) => SawExprBinary(op.node),
252-
ExprUnary(op, _) => SawExprUnary(op),
253-
ExprLit(ref lit) => SawExprLit(lit.node.clone()),
254-
ExprCast(..) => SawExprCast,
255-
ExprType(..) => SawExprType,
256-
ExprIf(..) => SawExprIf,
257-
ExprWhile(..) => SawExprWhile,
258-
ExprLoop(_, id) => SawExprLoop(id.map(|id| id.node.as_str())),
259-
ExprMatch(..) => SawExprMatch,
260-
ExprClosure(cc, _, _, _) => SawExprClosure(cc),
261-
ExprBlock(..) => SawExprBlock,
262-
ExprAssign(..) => SawExprAssign,
263-
ExprAssignOp(op, ..) => SawExprAssignOp(op.node),
264-
ExprField(_, name) => SawExprField(name.node.as_str()),
265-
ExprTupField(_, id) => SawExprTupField(id.node),
266-
ExprIndex(..) => SawExprIndex,
267-
ExprPath(ref qself, _) => SawExprPath(qself.as_ref().map(|q| q.position)),
268-
ExprAddrOf(m, _) => SawExprAddrOf(m),
269-
ExprBreak(id) => SawExprBreak(id.map(|id| id.node.as_str())),
270-
ExprAgain(id) => SawExprAgain(id.map(|id| id.node.as_str())),
271-
ExprRet(..) => SawExprRet,
272-
ExprInlineAsm(ref a,..) => SawExprInlineAsm(a),
273-
ExprStruct(..) => SawExprStruct,
274-
ExprRepeat(..) => SawExprRepeat,
288+
ExprBox(..) => (SawExprBox, false),
289+
ExprArray(..) => (SawExprArray, false),
290+
ExprCall(..) => (SawExprCall, false),
291+
ExprMethodCall(..) => (SawExprMethodCall, false),
292+
ExprTup(..) => (SawExprTup, false),
293+
ExprBinary(op, ..) => {
294+
(SawExprBinary(op.node), binop_can_panic_at_runtime(op.node))
295+
}
296+
ExprUnary(op, _) => {
297+
(SawExprUnary(op), unop_can_panic_at_runtime(op))
298+
}
299+
ExprLit(ref lit) => (SawExprLit(lit.node.clone()), false),
300+
ExprCast(..) => (SawExprCast, false),
301+
ExprType(..) => (SawExprType, false),
302+
ExprIf(..) => (SawExprIf, false),
303+
ExprWhile(..) => (SawExprWhile, false),
304+
ExprLoop(_, id) => (SawExprLoop(id.map(|id| id.node.as_str())), false),
305+
ExprMatch(..) => (SawExprMatch, false),
306+
ExprClosure(cc, _, _, _) => (SawExprClosure(cc), false),
307+
ExprBlock(..) => (SawExprBlock, false),
308+
ExprAssign(..) => (SawExprAssign, false),
309+
ExprAssignOp(op, ..) => {
310+
(SawExprAssignOp(op.node), binop_can_panic_at_runtime(op.node))
311+
}
312+
ExprField(_, name) => (SawExprField(name.node.as_str()), false),
313+
ExprTupField(_, id) => (SawExprTupField(id.node), false),
314+
ExprIndex(..) => (SawExprIndex, true),
315+
ExprPath(ref qself, _) => (SawExprPath(qself.as_ref().map(|q| q.position)), false),
316+
ExprAddrOf(m, _) => (SawExprAddrOf(m), false),
317+
ExprBreak(id) => (SawExprBreak(id.map(|id| id.node.as_str())), false),
318+
ExprAgain(id) => (SawExprAgain(id.map(|id| id.node.as_str())), false),
319+
ExprRet(..) => (SawExprRet, false),
320+
ExprInlineAsm(ref a,..) => (SawExprInlineAsm(a), false),
321+
ExprStruct(..) => (SawExprStruct, false),
322+
ExprRepeat(..) => (SawExprRepeat, false),
275323
}
276324
}
277325

@@ -421,10 +469,13 @@ macro_rules! hash_attrs {
421469

422470
macro_rules! hash_span {
423471
($visitor:expr, $span:expr) => ({
424-
if $visitor.hash_spans {
472+
hash_span!($visitor, $span, false)
473+
});
474+
($visitor:expr, $span:expr, $force:expr) => ({
475+
if $force || $visitor.hash_spans {
425476
$visitor.hash_span($span);
426477
}
427-
})
478+
});
428479
}
429480

430481
impl<'a, 'hash, 'tcx> visit::Visitor<'tcx> for StrictVersionHashVisitor<'a, 'hash, 'tcx> {
@@ -474,10 +525,12 @@ impl<'a, 'hash, 'tcx> visit::Visitor<'tcx> for StrictVersionHashVisitor<'a, 'has
474525

475526
fn visit_expr(&mut self, ex: &'tcx Expr) {
476527
debug!("visit_expr: st={:?}", self.st);
477-
SawExpr(saw_expr(&ex.node)).hash(self.st);
528+
let (saw_expr, force_span) = saw_expr(&ex.node,
529+
self.overflow_checks_enabled);
530+
SawExpr(saw_expr).hash(self.st);
478531
// No need to explicitly hash the discriminant here, since we are
479532
// implicitly hashing the discriminant of SawExprComponent.
480-
hash_span!(self, ex.span);
533+
hash_span!(self, ex.span, force_span);
481534
hash_attrs!(self, &ex.attrs);
482535
visit::walk_expr(self, ex)
483536
}
@@ -519,6 +572,9 @@ impl<'a, 'hash, 'tcx> visit::Visitor<'tcx> for StrictVersionHashVisitor<'a, 'has
519572

520573
fn visit_item(&mut self, i: &'tcx Item) {
521574
debug!("visit_item: {:?} st={:?}", i, self.st);
575+
576+
self.maybe_enable_overflow_checks(&i.attrs);
577+
522578
SawItem(saw_item(&i.node)).hash(self.st);
523579
hash_span!(self, i.span);
524580
hash_attrs!(self, &i.attrs);
@@ -545,6 +601,9 @@ impl<'a, 'hash, 'tcx> visit::Visitor<'tcx> for StrictVersionHashVisitor<'a, 'has
545601

546602
fn visit_trait_item(&mut self, ti: &'tcx TraitItem) {
547603
debug!("visit_trait_item: st={:?}", self.st);
604+
605+
self.maybe_enable_overflow_checks(&ti.attrs);
606+
548607
SawTraitItem(saw_trait_item(&ti.node)).hash(self.st);
549608
hash_span!(self, ti.span);
550609
hash_attrs!(self, &ti.attrs);
@@ -553,6 +612,9 @@ impl<'a, 'hash, 'tcx> visit::Visitor<'tcx> for StrictVersionHashVisitor<'a, 'has
553612

554613
fn visit_impl_item(&mut self, ii: &'tcx ImplItem) {
555614
debug!("visit_impl_item: st={:?}", self.st);
615+
616+
self.maybe_enable_overflow_checks(&ii.attrs);
617+
556618
SawImplItem(saw_impl_item(&ii.node)).hash(self.st);
557619
hash_span!(self, ii.span);
558620
hash_attrs!(self, &ii.attrs);
@@ -842,4 +904,10 @@ impl<'a, 'hash, 'tcx> StrictVersionHashVisitor<'a, 'hash, 'tcx> {
842904
indices.sort_by_key(|index| get_key(&items[*index]));
843905
indices
844906
}
907+
908+
fn maybe_enable_overflow_checks(&mut self, item_attrs: &[ast::Attribute]) {
909+
if attr::contains_name(item_attrs, "rustc_inherit_overflow_checks") {
910+
self.overflow_checks_enabled = true;
911+
}
912+
}
845913
}
+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// Copyright 2016 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+
// This test case tests the incremental compilation hash (ICH) implementation
12+
// for exprs that can panic at runtime (e.g. because of bounds checking). For
13+
// these expressions an error message containing their source location is
14+
// generated, so their hash must always depend on their location in the source
15+
// code, not just when debuginfo is enabled.
16+
17+
// The general pattern followed here is: Change one thing between rev1 and rev2
18+
// and make sure that the hash has changed, then change nothing between rev2 and
19+
// rev3 and make sure that the hash has not changed.
20+
21+
// must-compile-successfully
22+
// revisions: cfail1 cfail2 cfail3
23+
// compile-flags: -Z query-dep-graph -C debug-assertions
24+
25+
#![allow(warnings)]
26+
#![feature(rustc_attrs)]
27+
#![crate_type="rlib"]
28+
29+
30+
// Indexing expression ---------------------------------------------------------
31+
#[cfg(cfail1)]
32+
pub fn indexing(slice: &[u8]) -> u8 {
33+
slice[100]
34+
}
35+
36+
#[cfg(not(cfail1))]
37+
#[rustc_dirty(label="Hir", cfg="cfail2")]
38+
#[rustc_clean(label="Hir", cfg="cfail3")]
39+
#[rustc_metadata_dirty(cfg="cfail2")]
40+
#[rustc_metadata_clean(cfg="cfail3")]
41+
pub fn indexing(slice: &[u8]) -> u8 {
42+
slice[100]
43+
}
44+
45+
46+
// Arithmetic overflow plus ----------------------------------------------------
47+
#[cfg(cfail1)]
48+
pub fn arithmetic_overflow_plus(val: i32) -> i32 {
49+
val + 1
50+
}
51+
52+
#[cfg(not(cfail1))]
53+
#[rustc_dirty(label="Hir", cfg="cfail2")]
54+
#[rustc_clean(label="Hir", cfg="cfail3")]
55+
#[rustc_metadata_dirty(cfg="cfail2")]
56+
#[rustc_metadata_clean(cfg="cfail3")]
57+
pub fn arithmetic_overflow_plus(val: i32) -> i32 {
58+
val + 1
59+
}
60+
61+
62+
// Arithmetic overflow minus ----------------------------------------------------
63+
#[cfg(cfail1)]
64+
pub fn arithmetic_overflow_minus(val: i32) -> i32 {
65+
val - 1
66+
}
67+
68+
#[cfg(not(cfail1))]
69+
#[rustc_dirty(label="Hir", cfg="cfail2")]
70+
#[rustc_clean(label="Hir", cfg="cfail3")]
71+
#[rustc_metadata_dirty(cfg="cfail2")]
72+
#[rustc_metadata_clean(cfg="cfail3")]
73+
pub fn arithmetic_overflow_minus(val: i32) -> i32 {
74+
val - 1
75+
}
76+
77+
78+
// Arithmetic overflow mult ----------------------------------------------------
79+
#[cfg(cfail1)]
80+
pub fn arithmetic_overflow_mult(val: i32) -> i32 {
81+
val * 2
82+
}
83+
84+
#[cfg(not(cfail1))]
85+
#[rustc_dirty(label="Hir", cfg="cfail2")]
86+
#[rustc_clean(label="Hir", cfg="cfail3")]
87+
#[rustc_metadata_dirty(cfg="cfail2")]
88+
#[rustc_metadata_clean(cfg="cfail3")]
89+
pub fn arithmetic_overflow_mult(val: i32) -> i32 {
90+
val * 2
91+
}
92+
93+
94+
// Arithmetic overflow negation ------------------------------------------------
95+
#[cfg(cfail1)]
96+
pub fn arithmetic_overflow_negation(val: i32) -> i32 {
97+
-val
98+
}
99+
100+
#[cfg(not(cfail1))]
101+
#[rustc_dirty(label="Hir", cfg="cfail2")]
102+
#[rustc_clean(label="Hir", cfg="cfail3")]
103+
#[rustc_metadata_dirty(cfg="cfail2")]
104+
#[rustc_metadata_clean(cfg="cfail3")]
105+
pub fn arithmetic_overflow_negation(val: i32) -> i32 {
106+
-val
107+
}
108+
109+
110+
// Division by zero ------------------------------------------------------------
111+
#[cfg(cfail1)]
112+
pub fn division_by_zero(val: i32) -> i32 {
113+
2 / val
114+
}
115+
116+
#[cfg(not(cfail1))]
117+
#[rustc_dirty(label="Hir", cfg="cfail2")]
118+
#[rustc_clean(label="Hir", cfg="cfail3")]
119+
#[rustc_metadata_dirty(cfg="cfail2")]
120+
#[rustc_metadata_clean(cfg="cfail3")]
121+
pub fn division_by_zero(val: i32) -> i32 {
122+
2 / val
123+
}
124+
125+
// Division by zero ------------------------------------------------------------
126+
#[cfg(cfail1)]
127+
pub fn mod_by_zero(val: i32) -> i32 {
128+
2 % val
129+
}
130+
131+
#[cfg(not(cfail1))]
132+
#[rustc_dirty(label="Hir", cfg="cfail2")]
133+
#[rustc_clean(label="Hir", cfg="cfail3")]
134+
#[rustc_metadata_dirty(cfg="cfail2")]
135+
#[rustc_metadata_clean(cfg="cfail3")]
136+
pub fn mod_by_zero(val: i32) -> i32 {
137+
2 % val
138+
}
139+
140+
141+
142+
// THE FOLLOWING ITEMS SHOULD NOT BE INFLUENCED BY THEIR SOURCE LOCATION
143+
144+
// bitwise ---------------------------------------------------------------------
145+
#[cfg(cfail1)]
146+
pub fn bitwise(val: i32) -> i32 {
147+
!val & 0x101010101 | 0x45689 ^ 0x2372382 << 1 >> 1
148+
}
149+
150+
#[cfg(not(cfail1))]
151+
#[rustc_clean(label="Hir", cfg="cfail2")]
152+
#[rustc_clean(label="Hir", cfg="cfail3")]
153+
#[rustc_metadata_clean(cfg="cfail2")]
154+
#[rustc_metadata_clean(cfg="cfail3")]
155+
pub fn bitwise(val: i32) -> i32 {
156+
!val & 0x101010101 | 0x45689 ^ 0x2372382 << 1 >> 1
157+
}
158+
159+
160+
// logical ---------------------------------------------------------------------
161+
#[cfg(cfail1)]
162+
pub fn logical(val1: bool, val2: bool, val3: bool) -> bool {
163+
val1 && val2 || val3
164+
}
165+
166+
#[cfg(not(cfail1))]
167+
#[rustc_clean(label="Hir", cfg="cfail2")]
168+
#[rustc_clean(label="Hir", cfg="cfail3")]
169+
#[rustc_metadata_clean(cfg="cfail2")]
170+
#[rustc_metadata_clean(cfg="cfail3")]
171+
pub fn logical(val1: bool, val2: bool, val3: bool) -> bool {
172+
val1 && val2 || val3
173+
}

0 commit comments

Comments
 (0)