From e3025a07338eca4092f96c16011caccee149c8d4 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 31 Oct 2016 16:37:13 -0400 Subject: [PATCH] ICH: Hash expression spans if their source location is captured for panics --- .../calculate_svh/svh_visitor.rs | 138 ++++++++++---- src/test/incremental/hashes/panic_exprs.rs | 173 ++++++++++++++++++ 2 files changed, 276 insertions(+), 35 deletions(-) create mode 100644 src/test/incremental/hashes/panic_exprs.rs diff --git a/src/librustc_incremental/calculate_svh/svh_visitor.rs b/src/librustc_incremental/calculate_svh/svh_visitor.rs index 51c894e1b78..80c41f855ba 100644 --- a/src/librustc_incremental/calculate_svh/svh_visitor.rs +++ b/src/librustc_incremental/calculate_svh/svh_visitor.rs @@ -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; + } + } } diff --git a/src/test/incremental/hashes/panic_exprs.rs b/src/test/incremental/hashes/panic_exprs.rs new file mode 100644 index 00000000000..f5f4c0042b4 --- /dev/null +++ b/src/test/incremental/hashes/panic_exprs.rs @@ -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 or the MIT license +// , 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 +}