Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce HashStable trait and base ICH implementations on it. #40878

Merged
merged 1 commit into from
Apr 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/librustc/hir/map/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,10 @@ impl Definitions {
}
}

pub fn node_to_hir_id(&self, node_id: ast::NodeId) -> hir::HirId {
self.node_to_hir_id[node_id]
}

/// Add a definition with a parent definition.
pub fn create_def_with_parent(&mut self,
parent: Option<DefIndex>,
Expand Down
300 changes: 300 additions & 0 deletions src/librustc/ich/hcx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
// Copyright 2017 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.

use hir;
use hir::def_id::DefId;
use ich::{self, CachingCodemapView, DefPathHashes};
use session::config::DebugInfoLevel::NoDebugInfo;
use ty;

use std::hash as std_hash;

use syntax::ast;
use syntax::attr;
use syntax::ext::hygiene::SyntaxContext;
use syntax::symbol::Symbol;
use syntax_pos::Span;

use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
StableHasherResult};
use rustc_data_structures::accumulate_vec::AccumulateVec;

/// This is the context state available during incr. comp. hashing. It contains
/// enough information to transform DefIds and HirIds into stable DefPaths (i.e.
/// a reference to the TyCtxt) and it holds a few caches for speeding up various
/// things (e.g. each DefId/DefPath is only hashed once).
pub struct StableHashingContext<'a, 'tcx: 'a> {
tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
def_path_hashes: DefPathHashes<'a, 'tcx>,
codemap: CachingCodemapView<'tcx>,
hash_spans: bool,
hash_bodies: bool,
overflow_checks_enabled: bool,
node_id_hashing_mode: NodeIdHashingMode,
// A sorted array of symbol keys for fast lookup.
ignored_attr_names: Vec<Symbol>,
}

#[derive(PartialEq, Eq, Clone, Copy)]
pub enum NodeIdHashingMode {
Ignore,
HashDefPath,
HashTraitsInScope,
}

impl<'a, 'tcx: 'a> StableHashingContext<'a, 'tcx> {

pub fn new(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>) -> Self {
let hash_spans_initial = tcx.sess.opts.debuginfo != NoDebugInfo;
let check_overflow_initial = tcx.sess.overflow_checks();

let mut ignored_attr_names: Vec<_> = ich::IGNORED_ATTRIBUTES
.iter()
.map(|&s| Symbol::intern(s))
.collect();

ignored_attr_names.sort();

StableHashingContext {
tcx: tcx,
def_path_hashes: DefPathHashes::new(tcx),
codemap: CachingCodemapView::new(tcx),
hash_spans: hash_spans_initial,
hash_bodies: true,
overflow_checks_enabled: check_overflow_initial,
node_id_hashing_mode: NodeIdHashingMode::HashDefPath,
ignored_attr_names: ignored_attr_names,
}
}

#[inline]
pub fn while_hashing_hir_bodies<F: FnOnce(&mut Self)>(&mut self,
hash_bodies: bool,
f: F) {
let prev_hash_bodies = self.hash_bodies;
self.hash_bodies = hash_bodies;
f(self);
self.hash_bodies = prev_hash_bodies;
}

#[inline]
pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self,
hash_spans: bool,
f: F) {
let prev_hash_spans = self.hash_spans;
self.hash_spans = hash_spans;
f(self);
self.hash_spans = prev_hash_spans;
}

#[inline]
pub fn with_node_id_hashing_mode<F: FnOnce(&mut Self)>(&mut self,
mode: NodeIdHashingMode,
f: F) {
let prev = self.node_id_hashing_mode;
self.node_id_hashing_mode = mode;
f(self);
self.node_id_hashing_mode = prev;
}

#[inline]
pub fn tcx(&self) -> ty::TyCtxt<'a, 'tcx, 'tcx> {
self.tcx
}

#[inline]
pub fn def_path_hash(&mut self, def_id: DefId) -> u64 {
self.def_path_hashes.hash(def_id)
}

#[inline]
pub fn hash_spans(&self) -> bool {
self.hash_spans
}

#[inline]
pub fn hash_bodies(&self) -> bool {
self.hash_bodies
}

#[inline]
pub fn codemap(&mut self) -> &mut CachingCodemapView<'tcx> {
&mut self.codemap
}

#[inline]
pub fn is_ignored_attr(&self, name: Symbol) -> bool {
self.ignored_attr_names.binary_search(&name).is_ok()
}

pub fn hash_hir_item_like<F: FnOnce(&mut Self)>(&mut self,
item_attrs: &[ast::Attribute],
f: F) {
let prev_overflow_checks = self.overflow_checks_enabled;
if attr::contains_name(item_attrs, "rustc_inherit_overflow_checks") {
self.overflow_checks_enabled = true;
}
let prev_hash_node_ids = self.node_id_hashing_mode;
self.node_id_hashing_mode = NodeIdHashingMode::Ignore;

f(self);

self.node_id_hashing_mode = prev_hash_node_ids;
self.overflow_checks_enabled = prev_overflow_checks;
}

#[inline]
pub fn binop_can_panic_at_runtime(&self, binop: hir::BinOp_) -> bool
{
match binop {
hir::BiAdd |
hir::BiSub |
hir::BiMul => self.overflow_checks_enabled,

hir::BiDiv |
hir::BiRem => true,

hir::BiAnd |
hir::BiOr |
hir::BiBitXor |
hir::BiBitAnd |
hir::BiBitOr |
hir::BiShl |
hir::BiShr |
hir::BiEq |
hir::BiLt |
hir::BiLe |
hir::BiNe |
hir::BiGe |
hir::BiGt => false
}
}

#[inline]
pub fn unop_can_panic_at_runtime(&self, unop: hir::UnOp) -> bool
{
match unop {
hir::UnDeref |
hir::UnNot => false,
hir::UnNeg => self.overflow_checks_enabled,
}
}
}


impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for ast::NodeId {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'a, 'tcx>,
hasher: &mut StableHasher<W>) {
match hcx.node_id_hashing_mode {
NodeIdHashingMode::Ignore => {
// Most NodeIds in the HIR can be ignored, but if there is a
// corresponding entry in the `trait_map` we need to hash that.
// Make sure we don't ignore too much by checking that there is
// no entry in a debug_assert!().
debug_assert!(hcx.tcx.trait_map.get(self).is_none());
}
NodeIdHashingMode::HashDefPath => {
hcx.tcx.hir.definitions().node_to_hir_id(*self).hash_stable(hcx, hasher);
}
NodeIdHashingMode::HashTraitsInScope => {
if let Some(traits) = hcx.tcx.trait_map.get(self) {
// The ordering of the candidates is not fixed. So we hash
// the def-ids and then sort them and hash the collection.
let mut candidates: AccumulateVec<[_; 8]> =
traits.iter()
.map(|&hir::TraitCandidate { def_id, import_id: _ }| {
hcx.def_path_hash(def_id)
})
.collect();
if traits.len() > 1 {
candidates.sort();
}
candidates.hash_stable(hcx, hasher);
}
}
}
}
}

impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for Span {

// Hash a span in a stable way. We can't directly hash the span's BytePos
// fields (that would be similar to hashing pointers, since those are just
// offsets into the CodeMap). Instead, we hash the (file name, line, column)
// triple, which stays the same even if the containing FileMap has moved
// within the CodeMap.
// Also note that we are hashing byte offsets for the column, not unicode
// codepoint offsets. For the purpose of the hash that's sufficient.
// Also, hashing filenames is expensive so we avoid doing it twice when the
// span starts and ends in the same file, which is almost always the case.
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'a, 'tcx>,
hasher: &mut StableHasher<W>) {
use syntax_pos::Pos;

if !hcx.hash_spans {
return
}

// If this is not an empty or invalid span, we want to hash the last
// position that belongs to it, as opposed to hashing the first
// position past it.
let span_hi = if self.hi > self.lo {
// We might end up in the middle of a multibyte character here,
// but that's OK, since we are not trying to decode anything at
// this position.
self.hi - ::syntax_pos::BytePos(1)
} else {
self.hi
};

{
let loc1 = hcx.codemap().byte_pos_to_line_and_col(self.lo);
let loc1 = loc1.as_ref()
.map(|&(ref fm, line, col)| (&fm.name[..], line, col.to_usize()))
.unwrap_or(("???", 0, 0));

let loc2 = hcx.codemap().byte_pos_to_line_and_col(span_hi);
let loc2 = loc2.as_ref()
.map(|&(ref fm, line, col)| (&fm.name[..], line, col.to_usize()))
.unwrap_or(("???", 0, 0));

if loc1.0 == loc2.0 {
std_hash::Hash::hash(&0u8, hasher);

std_hash::Hash::hash(loc1.0, hasher);
std_hash::Hash::hash(&loc1.1, hasher);
std_hash::Hash::hash(&loc1.2, hasher);

// Do not hash the file name twice
std_hash::Hash::hash(&loc2.1, hasher);
std_hash::Hash::hash(&loc2.2, hasher);
} else {
std_hash::Hash::hash(&1u8, hasher);

std_hash::Hash::hash(loc1.0, hasher);
std_hash::Hash::hash(&loc1.1, hasher);
std_hash::Hash::hash(&loc1.2, hasher);

std_hash::Hash::hash(loc2.0, hasher);
std_hash::Hash::hash(&loc2.1, hasher);
std_hash::Hash::hash(&loc2.2, hasher);
}
}

if self.ctxt == SyntaxContext::empty() {
0u8.hash_stable(hcx, hasher);
} else {
1u8.hash_stable(hcx, hasher);
self.source_callsite().hash_stable(hcx, hasher);
}
}
}
71 changes: 71 additions & 0 deletions src/librustc/ich/impls_const_math.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2017 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 module contains `HashStable` implementations for various data types
//! from `rustc_const_math` in no particular order.

impl_stable_hash_for!(enum ::rustc_const_math::ConstFloat {
F32(val),
F64(val)
});

impl_stable_hash_for!(enum ::rustc_const_math::ConstInt {
I8(val),
I16(val),
I32(val),
I64(val),
I128(val),
Isize(val),
U8(val),
U16(val),
U32(val),
U64(val),
U128(val),
Usize(val)
});

impl_stable_hash_for!(enum ::rustc_const_math::ConstIsize {
Is16(i16),
Is32(i32),
Is64(i64)
});

impl_stable_hash_for!(enum ::rustc_const_math::ConstUsize {
Us16(i16),
Us32(i32),
Us64(i64)
});

impl_stable_hash_for!(enum ::rustc_const_math::ConstMathErr {
NotInRange,
CmpBetweenUnequalTypes,
UnequalTypes(op),
Overflow(op),
ShiftNegative,
DivisionByZero,
RemainderByZero,
UnsignedNegation,
ULitOutOfRange(int_ty),
LitOutOfRange(int_ty)
});

impl_stable_hash_for!(enum ::rustc_const_math::Op {
Add,
Sub,
Mul,
Div,
Rem,
Shr,
Shl,
Neg,
BitAnd,
BitOr,
BitXor
});
Loading