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;
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> {
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,
}
}
// 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
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),
}
}
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> {
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)
}
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);
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);
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);
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;
+ }
+ }
}
--- /dev/null
+// 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
+}