]> git.lizzy.rs Git - rust.git/commitdiff
ICH: Hash expression spans if their source location is captured for panics
authorMichael Woerister <michaelwoerister@posteo.net>
Mon, 31 Oct 2016 20:37:13 +0000 (16:37 -0400)
committerMichael Woerister <michaelwoerister@posteo.net>
Tue, 1 Nov 2016 13:41:46 +0000 (09:41 -0400)
src/librustc_incremental/calculate_svh/svh_visitor.rs
src/test/incremental/hashes/panic_exprs.rs [new file with mode: 0644]

index 51c894e1b78f04a99ad322bc5a7dd68432580b4d..80c41f855ba5c95508e60369a9020879bc650e77 100644 (file)
@@ -21,6 +21,7 @@
 use self::SawTraitOrImplItemComponent::*;
 use syntax::abi::Abi;
 use syntax::ast::{self, Name, NodeId};
+use syntax::attr;
 use syntax::parse::token;
 use syntax_pos::{Span, NO_EXPANSION, COMMAND_LINE_EXPN, BytePos};
 use rustc::hir;
@@ -53,6 +54,7 @@ pub struct StrictVersionHashVisitor<'a, 'hash: 'a, 'tcx: 'hash> {
     def_path_hashes: &'a mut DefPathHashes<'hash, 'tcx>,
     hash_spans: bool,
     codemap: &'a mut CachingCodemapView<'tcx>,
+    overflow_checks_enabled: bool,
 }
 
 impl<'a, 'hash, 'tcx> StrictVersionHashVisitor<'a, 'hash, 'tcx> {
@@ -62,12 +64,16 @@ pub fn new(st: &'a mut IchHasher,
                codemap: &'a mut CachingCodemapView<'tcx>,
                hash_spans: bool)
                -> Self {
+        let check_overflow = tcx.sess.opts.debugging_opts.force_overflow_checks
+            .unwrap_or(tcx.sess.opts.debug_assertions);
+
         StrictVersionHashVisitor {
             st: st,
             tcx: tcx,
             def_path_hashes: def_path_hashes,
             hash_spans: hash_spans,
             codemap: codemap,
+            overflow_checks_enabled: check_overflow,
         }
     }
 
@@ -83,7 +89,6 @@ fn compute_def_id_hash(&mut self, def_id: DefId) -> u64 {
     // Also note that we are hashing byte offsets for the column, not unicode
     // codepoint offsets. For the purpose of the hash that's sufficient.
     fn hash_span(&mut self, span: Span) {
-        debug_assert!(self.hash_spans);
         debug!("hash_span: st={:?}", self.st);
 
         // If this is not an empty or invalid span, we want to hash the last
@@ -241,37 +246,80 @@ enum SawExprComponent<'a> {
     SawExprRepeat,
 }
 
-fn saw_expr<'a>(node: &'a Expr_) -> SawExprComponent<'a> {
+// The boolean returned indicates whether the span of this expression is always
+// significant, regardless of debuginfo.
+fn saw_expr<'a>(node: &'a Expr_,
+                overflow_checks_enabled: bool)
+                -> (SawExprComponent<'a>, bool) {
+    let binop_can_panic_at_runtime = |binop| {
+        match binop {
+            BiAdd |
+            BiSub |
+            BiMul => overflow_checks_enabled,
+
+            BiDiv |
+            BiRem => true,
+
+            BiAnd |
+            BiOr |
+            BiBitXor |
+            BiBitAnd |
+            BiBitOr |
+            BiShl |
+            BiShr |
+            BiEq |
+            BiLt |
+            BiLe |
+            BiNe |
+            BiGe |
+            BiGt => false
+        }
+    };
+
+    let unop_can_panic_at_runtime = |unop| {
+        match unop {
+            UnDeref |
+            UnNot => false,
+            UnNeg => overflow_checks_enabled,
+        }
+    };
+
     match *node {
-        ExprBox(..)              => SawExprBox,
-        ExprArray(..)            => SawExprArray,
-        ExprCall(..)             => SawExprCall,
-        ExprMethodCall(..)       => SawExprMethodCall,
-        ExprTup(..)              => SawExprTup,
-        ExprBinary(op, ..)       => SawExprBinary(op.node),
-        ExprUnary(op, _)         => SawExprUnary(op),
-        ExprLit(ref lit)         => SawExprLit(lit.node.clone()),
-        ExprCast(..)             => SawExprCast,
-        ExprType(..)             => SawExprType,
-        ExprIf(..)               => SawExprIf,
-        ExprWhile(..)            => SawExprWhile,
-        ExprLoop(_, id)          => SawExprLoop(id.map(|id| id.node.as_str())),
-        ExprMatch(..)            => SawExprMatch,
-        ExprClosure(cc, _, _, _) => SawExprClosure(cc),
-        ExprBlock(..)            => SawExprBlock,
-        ExprAssign(..)           => SawExprAssign,
-        ExprAssignOp(op, ..)     => SawExprAssignOp(op.node),
-        ExprField(_, name)       => SawExprField(name.node.as_str()),
-        ExprTupField(_, id)      => SawExprTupField(id.node),
-        ExprIndex(..)            => SawExprIndex,
-        ExprPath(ref qself, _)   => SawExprPath(qself.as_ref().map(|q| q.position)),
-        ExprAddrOf(m, _)         => SawExprAddrOf(m),
-        ExprBreak(id)            => SawExprBreak(id.map(|id| id.node.as_str())),
-        ExprAgain(id)            => SawExprAgain(id.map(|id| id.node.as_str())),
-        ExprRet(..)              => SawExprRet,
-        ExprInlineAsm(ref a,..)  => SawExprInlineAsm(a),
-        ExprStruct(..)           => SawExprStruct,
-        ExprRepeat(..)           => SawExprRepeat,
+        ExprBox(..)              => (SawExprBox, false),
+        ExprArray(..)            => (SawExprArray, false),
+        ExprCall(..)             => (SawExprCall, false),
+        ExprMethodCall(..)       => (SawExprMethodCall, false),
+        ExprTup(..)              => (SawExprTup, false),
+        ExprBinary(op, ..)       => {
+            (SawExprBinary(op.node), binop_can_panic_at_runtime(op.node))
+        }
+        ExprUnary(op, _)         => {
+            (SawExprUnary(op), unop_can_panic_at_runtime(op))
+        }
+        ExprLit(ref lit)         => (SawExprLit(lit.node.clone()), false),
+        ExprCast(..)             => (SawExprCast, false),
+        ExprType(..)             => (SawExprType, false),
+        ExprIf(..)               => (SawExprIf, false),
+        ExprWhile(..)            => (SawExprWhile, false),
+        ExprLoop(_, id)          => (SawExprLoop(id.map(|id| id.node.as_str())), false),
+        ExprMatch(..)            => (SawExprMatch, false),
+        ExprClosure(cc, _, _, _) => (SawExprClosure(cc), false),
+        ExprBlock(..)            => (SawExprBlock, false),
+        ExprAssign(..)           => (SawExprAssign, false),
+        ExprAssignOp(op, ..)     => {
+            (SawExprAssignOp(op.node), binop_can_panic_at_runtime(op.node))
+        }
+        ExprField(_, name)       => (SawExprField(name.node.as_str()), false),
+        ExprTupField(_, id)      => (SawExprTupField(id.node), false),
+        ExprIndex(..)            => (SawExprIndex, true),
+        ExprPath(ref qself, _)   => (SawExprPath(qself.as_ref().map(|q| q.position)), false),
+        ExprAddrOf(m, _)         => (SawExprAddrOf(m), false),
+        ExprBreak(id)            => (SawExprBreak(id.map(|id| id.node.as_str())), false),
+        ExprAgain(id)            => (SawExprAgain(id.map(|id| id.node.as_str())), false),
+        ExprRet(..)              => (SawExprRet, false),
+        ExprInlineAsm(ref a,..)  => (SawExprInlineAsm(a), false),
+        ExprStruct(..)           => (SawExprStruct, false),
+        ExprRepeat(..)           => (SawExprRepeat, false),
     }
 }
 
@@ -421,10 +469,13 @@ macro_rules! hash_attrs {
 
 macro_rules! hash_span {
     ($visitor:expr, $span:expr) => ({
-        if $visitor.hash_spans {
+        hash_span!($visitor, $span, false)
+    });
+    ($visitor:expr, $span:expr, $force:expr) => ({
+        if $force || $visitor.hash_spans {
             $visitor.hash_span($span);
         }
-    })
+    });
 }
 
 impl<'a, 'hash, 'tcx> visit::Visitor<'tcx> for StrictVersionHashVisitor<'a, 'hash, 'tcx> {
@@ -474,10 +525,12 @@ fn visit_lifetime_def(&mut self, l: &'tcx LifetimeDef) {
 
     fn visit_expr(&mut self, ex: &'tcx Expr) {
         debug!("visit_expr: st={:?}", self.st);
-        SawExpr(saw_expr(&ex.node)).hash(self.st);
+        let (saw_expr, force_span) = saw_expr(&ex.node,
+                                              self.overflow_checks_enabled);
+        SawExpr(saw_expr).hash(self.st);
         // No need to explicitly hash the discriminant here, since we are
         // implicitly hashing the discriminant of SawExprComponent.
-        hash_span!(self, ex.span);
+        hash_span!(self, ex.span, force_span);
         hash_attrs!(self, &ex.attrs);
         visit::walk_expr(self, ex)
     }
@@ -519,6 +572,9 @@ fn visit_foreign_item(&mut self, i: &'tcx ForeignItem) {
 
     fn visit_item(&mut self, i: &'tcx Item) {
         debug!("visit_item: {:?} st={:?}", i, self.st);
+
+        self.maybe_enable_overflow_checks(&i.attrs);
+
         SawItem(saw_item(&i.node)).hash(self.st);
         hash_span!(self, i.span);
         hash_attrs!(self, &i.attrs);
@@ -545,6 +601,9 @@ fn visit_generics(&mut self, g: &'tcx Generics) {
 
     fn visit_trait_item(&mut self, ti: &'tcx TraitItem) {
         debug!("visit_trait_item: st={:?}", self.st);
+
+        self.maybe_enable_overflow_checks(&ti.attrs);
+
         SawTraitItem(saw_trait_item(&ti.node)).hash(self.st);
         hash_span!(self, ti.span);
         hash_attrs!(self, &ti.attrs);
@@ -553,6 +612,9 @@ fn visit_trait_item(&mut self, ti: &'tcx TraitItem) {
 
     fn visit_impl_item(&mut self, ii: &'tcx ImplItem) {
         debug!("visit_impl_item: st={:?}", self.st);
+
+        self.maybe_enable_overflow_checks(&ii.attrs);
+
         SawImplItem(saw_impl_item(&ii.node)).hash(self.st);
         hash_span!(self, ii.span);
         hash_attrs!(self, &ii.attrs);
@@ -842,4 +904,10 @@ fn indices_sorted_by<T, K, F>(&mut self, items: &[T], get_key: F) -> Vec<usize>
         indices.sort_by_key(|index| get_key(&items[*index]));
         indices
     }
+
+    fn maybe_enable_overflow_checks(&mut self, item_attrs: &[ast::Attribute]) {
+        if attr::contains_name(item_attrs, "rustc_inherit_overflow_checks") {
+            self.overflow_checks_enabled = true;
+        }
+    }
 }
diff --git a/src/test/incremental/hashes/panic_exprs.rs b/src/test/incremental/hashes/panic_exprs.rs
new file mode 100644 (file)
index 0000000..f5f4c00
--- /dev/null
@@ -0,0 +1,173 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// This test case tests the incremental compilation hash (ICH) implementation
+// for exprs that can panic at runtime (e.g. because of bounds checking). For
+// these expressions an error message containing their source location is
+// generated, so their hash must always depend on their location in the source
+// code, not just when debuginfo is enabled.
+
+// The general pattern followed here is: Change one thing between rev1 and rev2
+// and make sure that the hash has changed, then change nothing between rev2 and
+// rev3 and make sure that the hash has not changed.
+
+// must-compile-successfully
+// revisions: cfail1 cfail2 cfail3
+// compile-flags: -Z query-dep-graph -C debug-assertions
+
+#![allow(warnings)]
+#![feature(rustc_attrs)]
+#![crate_type="rlib"]
+
+
+// Indexing expression ---------------------------------------------------------
+#[cfg(cfail1)]
+pub fn indexing(slice: &[u8]) -> u8 {
+    slice[100]
+}
+
+#[cfg(not(cfail1))]
+#[rustc_dirty(label="Hir", cfg="cfail2")]
+#[rustc_clean(label="Hir", cfg="cfail3")]
+#[rustc_metadata_dirty(cfg="cfail2")]
+#[rustc_metadata_clean(cfg="cfail3")]
+pub fn indexing(slice: &[u8]) -> u8 {
+    slice[100]
+}
+
+
+// Arithmetic overflow plus ----------------------------------------------------
+#[cfg(cfail1)]
+pub fn arithmetic_overflow_plus(val: i32) -> i32 {
+    val + 1
+}
+
+#[cfg(not(cfail1))]
+#[rustc_dirty(label="Hir", cfg="cfail2")]
+#[rustc_clean(label="Hir", cfg="cfail3")]
+#[rustc_metadata_dirty(cfg="cfail2")]
+#[rustc_metadata_clean(cfg="cfail3")]
+pub fn arithmetic_overflow_plus(val: i32) -> i32 {
+    val + 1
+}
+
+
+// Arithmetic overflow minus ----------------------------------------------------
+#[cfg(cfail1)]
+pub fn arithmetic_overflow_minus(val: i32) -> i32 {
+    val - 1
+}
+
+#[cfg(not(cfail1))]
+#[rustc_dirty(label="Hir", cfg="cfail2")]
+#[rustc_clean(label="Hir", cfg="cfail3")]
+#[rustc_metadata_dirty(cfg="cfail2")]
+#[rustc_metadata_clean(cfg="cfail3")]
+pub fn arithmetic_overflow_minus(val: i32) -> i32 {
+    val - 1
+}
+
+
+// Arithmetic overflow mult ----------------------------------------------------
+#[cfg(cfail1)]
+pub fn arithmetic_overflow_mult(val: i32) -> i32 {
+    val * 2
+}
+
+#[cfg(not(cfail1))]
+#[rustc_dirty(label="Hir", cfg="cfail2")]
+#[rustc_clean(label="Hir", cfg="cfail3")]
+#[rustc_metadata_dirty(cfg="cfail2")]
+#[rustc_metadata_clean(cfg="cfail3")]
+pub fn arithmetic_overflow_mult(val: i32) -> i32 {
+    val * 2
+}
+
+
+// Arithmetic overflow negation ------------------------------------------------
+#[cfg(cfail1)]
+pub fn arithmetic_overflow_negation(val: i32) -> i32 {
+    -val
+}
+
+#[cfg(not(cfail1))]
+#[rustc_dirty(label="Hir", cfg="cfail2")]
+#[rustc_clean(label="Hir", cfg="cfail3")]
+#[rustc_metadata_dirty(cfg="cfail2")]
+#[rustc_metadata_clean(cfg="cfail3")]
+pub fn arithmetic_overflow_negation(val: i32) -> i32 {
+    -val
+}
+
+
+// Division by zero ------------------------------------------------------------
+#[cfg(cfail1)]
+pub fn division_by_zero(val: i32) -> i32 {
+    2 / val
+}
+
+#[cfg(not(cfail1))]
+#[rustc_dirty(label="Hir", cfg="cfail2")]
+#[rustc_clean(label="Hir", cfg="cfail3")]
+#[rustc_metadata_dirty(cfg="cfail2")]
+#[rustc_metadata_clean(cfg="cfail3")]
+pub fn division_by_zero(val: i32) -> i32 {
+    2 / val
+}
+
+// Division by zero ------------------------------------------------------------
+#[cfg(cfail1)]
+pub fn mod_by_zero(val: i32) -> i32 {
+    2 % val
+}
+
+#[cfg(not(cfail1))]
+#[rustc_dirty(label="Hir", cfg="cfail2")]
+#[rustc_clean(label="Hir", cfg="cfail3")]
+#[rustc_metadata_dirty(cfg="cfail2")]
+#[rustc_metadata_clean(cfg="cfail3")]
+pub fn mod_by_zero(val: i32) -> i32 {
+    2 % val
+}
+
+
+
+// THE FOLLOWING ITEMS SHOULD NOT BE INFLUENCED BY THEIR SOURCE LOCATION
+
+// bitwise ---------------------------------------------------------------------
+#[cfg(cfail1)]
+pub fn bitwise(val: i32) -> i32 {
+    !val & 0x101010101 | 0x45689 ^ 0x2372382 << 1 >> 1
+}
+
+#[cfg(not(cfail1))]
+#[rustc_clean(label="Hir", cfg="cfail2")]
+#[rustc_clean(label="Hir", cfg="cfail3")]
+#[rustc_metadata_clean(cfg="cfail2")]
+#[rustc_metadata_clean(cfg="cfail3")]
+pub fn bitwise(val: i32) -> i32 {
+    !val & 0x101010101 | 0x45689 ^ 0x2372382 << 1 >> 1
+}
+
+
+// logical ---------------------------------------------------------------------
+#[cfg(cfail1)]
+pub fn logical(val1: bool, val2: bool, val3: bool) -> bool {
+    val1 && val2 || val3
+}
+
+#[cfg(not(cfail1))]
+#[rustc_clean(label="Hir", cfg="cfail2")]
+#[rustc_clean(label="Hir", cfg="cfail3")]
+#[rustc_metadata_clean(cfg="cfail2")]
+#[rustc_metadata_clean(cfg="cfail3")]
+pub fn logical(val1: bool, val2: bool, val3: bool) -> bool {
+    val1 && val2 || val3
+}