ICH: Hash expression spans if their source location is captured for panics

This commit is contained in:
Michael Woerister 2016-10-31 16:37:13 -04:00
parent bf5b824c1c
commit e3025a0733
2 changed files with 276 additions and 35 deletions

View File

@ -21,6 +21,7 @@ use self::SawTyComponent::*;
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 @@ 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,
}
}
@ -83,7 +89,6 @@ impl<'a, 'hash, 'tcx> StrictVersionHashVisitor<'a, 'hash, 'tcx> {
// 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 @@ impl<'a, 'hash, 'tcx> visit::Visitor<'tcx> for StrictVersionHashVisitor<'a, 'has
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 @@ impl<'a, 'hash, 'tcx> visit::Visitor<'tcx> for StrictVersionHashVisitor<'a, 'has
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 @@ impl<'a, 'hash, 'tcx> visit::Visitor<'tcx> for StrictVersionHashVisitor<'a, 'has
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 @@ impl<'a, 'hash, 'tcx> visit::Visitor<'tcx> for StrictVersionHashVisitor<'a, 'has
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 @@ impl<'a, 'hash, 'tcx> StrictVersionHashVisitor<'a, 'hash, 'tcx> {
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;
}
}
}

View File

@ -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
}