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

Rewrite TypeId computation to not miss anything and work cross-crate. #35267

Merged
merged 1 commit into from
Aug 6, 2016
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
289 changes: 146 additions & 143 deletions src/librustc/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,22 @@

//! misc. type-system utilities too small to deserve their own file

use hir::svh::Svh;
use hir::def_id::DefId;
use ty::subst;
use infer::InferCtxt;
use hir::pat_util;
use traits::{self, ProjectionMode};
use ty::{self, Ty, TyCtxt, TypeAndMut, TypeFlags, TypeFoldable};
use ty::{Disr, ParameterEnvironment};
use ty::fold::TypeVisitor;
use ty::layout::{Layout, LayoutError};
use ty::TypeVariants::*;

use rustc_const_math::{ConstInt, ConstIsize, ConstUsize};

use std::cmp;
use std::hash::{Hash, SipHasher, Hasher};
use std::intrinsics;
use syntax::ast::{self, Name};
use syntax::attr::{self, SignedInt, UnsignedInt};
use syntax_pos::Span;
Expand Down Expand Up @@ -350,148 +351,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {

/// Creates a hash of the type `Ty` which will be the same no matter what crate
/// context it's calculated within. This is used by the `type_id` intrinsic.
pub fn hash_crate_independent(self, ty: Ty<'tcx>, svh: &Svh) -> u64 {
let mut state = SipHasher::new();
helper(self, ty, svh, &mut state);
return state.finish();

fn helper<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
ty: Ty<'tcx>, svh: &Svh,
state: &mut SipHasher) {
macro_rules! byte { ($b:expr) => { ($b as u8).hash(state) } }
macro_rules! hash { ($e:expr) => { $e.hash(state) } }

let region = |state: &mut SipHasher, r: ty::Region| {
match r {
ty::ReStatic | ty::ReErased => {}
ty::ReLateBound(db, ty::BrAnon(i)) => {
db.hash(state);
i.hash(state);
}
ty::ReEmpty |
ty::ReEarlyBound(..) |
ty::ReLateBound(..) |
ty::ReFree(..) |
ty::ReScope(..) |
ty::ReVar(..) |
ty::ReSkolemized(..) => {
bug!("unexpected region found when hashing a type")
}
}
};
let did = |state: &mut SipHasher, did: DefId| {
let h = if did.is_local() {
svh.clone()
} else {
tcx.sess.cstore.crate_hash(did.krate)
};
h.hash(state);
did.index.hash(state);
};
let mt = |state: &mut SipHasher, mt: TypeAndMut| {
mt.mutbl.hash(state);
};
let fn_sig = |state: &mut SipHasher, sig: &ty::Binder<ty::FnSig<'tcx>>| {
let sig = tcx.anonymize_late_bound_regions(sig).0;
for a in &sig.inputs { helper(tcx, *a, svh, state); }
if let ty::FnConverging(output) = sig.output {
helper(tcx, output, svh, state);
}
};
ty.maybe_walk(|ty| {
match ty.sty {
TyBool => byte!(2),
TyChar => byte!(3),
TyInt(i) => {
byte!(4);
hash!(i);
}
TyUint(u) => {
byte!(5);
hash!(u);
}
TyFloat(f) => {
byte!(6);
hash!(f);
}
TyStr => {
byte!(7);
}
TyEnum(d, _) => {
byte!(8);
did(state, d.did);
}
TyBox(_) => {
byte!(9);
}
TyArray(_, n) => {
byte!(10);
n.hash(state);
}
TySlice(_) => {
byte!(11);
}
TyRawPtr(m) => {
byte!(12);
mt(state, m);
}
TyRef(r, m) => {
byte!(13);
region(state, *r);
mt(state, m);
}
TyFnDef(def_id, _, _) => {
byte!(14);
hash!(def_id);
}
TyFnPtr(ref b) => {
byte!(15);
hash!(b.unsafety);
hash!(b.abi);
fn_sig(state, &b.sig);
return false;
}
TyTrait(ref data) => {
byte!(17);
did(state, data.principal_def_id());
hash!(data.bounds);

let principal = tcx.anonymize_late_bound_regions(&data.principal).0;
for subty in &principal.substs.types {
helper(tcx, subty, svh, state);
}

return false;
}
TyStruct(d, _) => {
byte!(18);
did(state, d.did);
}
TyTuple(ref inner) => {
byte!(19);
hash!(inner.len());
}
TyParam(p) => {
byte!(20);
hash!(p.space);
hash!(p.idx);
hash!(p.name.as_str());
}
TyInfer(_) => bug!(),
TyError => byte!(21),
TyClosure(d, _) => {
byte!(22);
did(state, d);
}
TyProjection(ref data) => {
byte!(23);
did(state, data.trait_ref.def_id);
hash!(data.item_name.as_str());
}
}
true
});
}
pub fn type_id_hash(self, ty: Ty<'tcx>) -> u64 {
let mut hasher = TypeIdHasher {
tcx: self,
state: SipHasher::new()
};
hasher.visit_ty(ty);
hasher.state.finish()
}

/// Returns true if this ADT is a dtorck type.
Expand Down Expand Up @@ -525,6 +391,143 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}
}

struct TypeIdHasher<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
state: SipHasher
}

impl<'a, 'gcx, 'tcx> TypeIdHasher<'a, 'gcx, 'tcx> {
fn hash<T: Hash>(&mut self, x: T) {
x.hash(&mut self.state);
}

fn hash_discriminant_u8<T>(&mut self, x: &T) {
let v = unsafe {
intrinsics::discriminant_value(x)
};
let b = v as u8;
assert_eq!(v, b as u64);
self.hash(b)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that I care, but why cast to u8 anyhow? I guess for portability? (Could also use to_le or whatever.) In any case, worth leaving a comment, since it's non-obvious.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was done like this before. My guess is to avoid dumping unnecessary zeroes into the hash, all the time, although a varint scheme would be better there. Not sure what the impact is.

}

fn def_id(&mut self, did: DefId) {
// Hash the crate identification information.
let name = self.tcx.crate_name(did.krate);
let disambiguator = self.tcx.crate_disambiguator(did.krate);
self.hash((name, disambiguator));

// Hash the item path within that crate.
// FIXME(#35379) This should use a deterministic
// DefPath hashing mechanism, not the DefIndex.
self.hash(did.index);
}
}

impl<'a, 'gcx, 'tcx> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx> {
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
// Distinguish between the Ty variants uniformly.
self.hash_discriminant_u8(&ty.sty);

match ty.sty {
TyInt(i) => self.hash(i),
TyUint(u) => self.hash(u),
TyFloat(f) => self.hash(f),
TyStruct(d, _) |
TyEnum(d, _) => self.def_id(d.did),
TyArray(_, n) => self.hash(n),
TyRawPtr(m) |
TyRef(_, m) => self.hash(m.mutbl),
TyClosure(def_id, _) |
TyFnDef(def_id, _, _) => self.def_id(def_id),
TyFnPtr(f) => {
self.hash(f.unsafety);
self.hash(f.abi);
self.hash(f.sig.variadic());
}
TyTrait(ref data) => {
// Trait objects have a list of projection bounds
// that are not guaranteed to be sorted in an order
// that gets preserved across crates, so we need
// to sort them again by the name, in string form.

// Hash the whole principal trait ref.
self.def_id(data.principal_def_id());
data.principal.visit_with(self);

// Hash region and builtin bounds.
data.bounds.region_bound.visit_with(self);
self.hash(data.bounds.builtin_bounds);

// Only projection bounds are left, sort and hash them.
let mut projection_bounds: Vec<_> = data.bounds.projection_bounds
.iter()
.map(|b| (b.item_name().as_str(), b))
.collect();
projection_bounds.sort_by_key(|&(ref name, _)| name.clone());
for (name, bound) in projection_bounds {
self.def_id(bound.0.projection_ty.trait_ref.def_id);
self.hash(name);
bound.visit_with(self);
}

// Bypass super_visit_with, we've visited everything.
return false;
}
TyTuple(tys) => {
self.hash(tys.len());
}
TyParam(p) => {
self.hash(p.space);
self.hash(p.idx);
self.hash(p.name.as_str());
}
TyProjection(ref data) => {
self.def_id(data.trait_ref.def_id);
self.hash(data.item_name.as_str());
}
TyBool |
TyChar |
TyStr |
TyBox(_) |
TySlice(_) |
TyError => {}
TyInfer(_) => bug!()
}

ty.super_visit_with(self)
}

fn visit_region(&mut self, r: ty::Region) -> bool {
match r {
ty::ReStatic | ty::ReErased => {
self.hash::<u32>(0);
}
ty::ReLateBound(db, ty::BrAnon(i)) => {
assert!(db.depth > 0);
self.hash::<u32>(db.depth);
self.hash(i);
}
ty::ReEmpty |
ty::ReEarlyBound(..) |
ty::ReLateBound(..) |
ty::ReFree(..) |
ty::ReScope(..) |
ty::ReVar(..) |
ty::ReSkolemized(..) => {
bug!("unexpected region found when hashing a type")
}
}
false
}

fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, x: &ty::Binder<T>) -> bool {
// Anonymize late-bound regions so that, for example:
// `for<'a, b> fn(&'a &'b T)` and `for<'a, b> fn(&'b &'a T)`
// result in the same TypeId (the two types are equivalent).
self.tcx.anonymize_late_bound_regions(x).super_visit_with(self)
}
}

impl<'a, 'tcx> ty::TyS<'tcx> {
fn impls_bound(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: &ParameterEnvironment<'tcx>,
Expand Down
4 changes: 1 addition & 3 deletions src/librustc_trans/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,9 +406,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
C_str_slice(ccx, ty_name)
}
(_, "type_id") => {
let hash = ccx.tcx().hash_crate_independent(*substs.types.get(FnSpace, 0),
&ccx.link_meta().crate_hash);
C_u64(ccx, hash)
C_u64(ccx, ccx.tcx().type_id_hash(*substs.types.get(FnSpace, 0)))
}
(_, "init_dropped") => {
let tp_ty = *substs.types.get(FnSpace, 0);
Expand Down
20 changes: 11 additions & 9 deletions src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ pub struct E(Result<&'static str, isize>);
pub type F = Option<isize>;
pub type G = usize;
pub type H = &'static str;
pub type I = Box<Fn()>;

pub unsafe fn id_A() -> TypeId { TypeId::of::<A>() }
pub unsafe fn id_B() -> TypeId { TypeId::of::<B>() }
pub unsafe fn id_C() -> TypeId { TypeId::of::<C>() }
pub unsafe fn id_D() -> TypeId { TypeId::of::<D>() }
pub unsafe fn id_E() -> TypeId { TypeId::of::<E>() }
pub unsafe fn id_F() -> TypeId { TypeId::of::<F>() }
pub unsafe fn id_G() -> TypeId { TypeId::of::<G>() }
pub unsafe fn id_H() -> TypeId { TypeId::of::<H>() }
pub fn id_A() -> TypeId { TypeId::of::<A>() }
pub fn id_B() -> TypeId { TypeId::of::<B>() }
pub fn id_C() -> TypeId { TypeId::of::<C>() }
pub fn id_D() -> TypeId { TypeId::of::<D>() }
pub fn id_E() -> TypeId { TypeId::of::<E>() }
pub fn id_F() -> TypeId { TypeId::of::<F>() }
pub fn id_G() -> TypeId { TypeId::of::<G>() }
pub fn id_H() -> TypeId { TypeId::of::<H>() }
pub fn id_I() -> TypeId { TypeId::of::<I>() }

pub unsafe fn foo<T: Any>() -> TypeId { TypeId::of::<T>() }
pub fn foo<T: Any>() -> TypeId { TypeId::of::<T>() }
20 changes: 11 additions & 9 deletions src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ pub struct E(Result<&'static str, isize>);
pub type F = Option<isize>;
pub type G = usize;
pub type H = &'static str;
pub type I = Box<Fn()>;

pub unsafe fn id_A() -> TypeId { TypeId::of::<A>() }
pub unsafe fn id_B() -> TypeId { TypeId::of::<B>() }
pub unsafe fn id_C() -> TypeId { TypeId::of::<C>() }
pub unsafe fn id_D() -> TypeId { TypeId::of::<D>() }
pub unsafe fn id_E() -> TypeId { TypeId::of::<E>() }
pub unsafe fn id_F() -> TypeId { TypeId::of::<F>() }
pub unsafe fn id_G() -> TypeId { TypeId::of::<G>() }
pub unsafe fn id_H() -> TypeId { TypeId::of::<H>() }
pub fn id_A() -> TypeId { TypeId::of::<A>() }
pub fn id_B() -> TypeId { TypeId::of::<B>() }
pub fn id_C() -> TypeId { TypeId::of::<C>() }
pub fn id_D() -> TypeId { TypeId::of::<D>() }
pub fn id_E() -> TypeId { TypeId::of::<E>() }
pub fn id_F() -> TypeId { TypeId::of::<F>() }
pub fn id_G() -> TypeId { TypeId::of::<G>() }
pub fn id_H() -> TypeId { TypeId::of::<H>() }
pub fn id_I() -> TypeId { TypeId::of::<I>() }

pub unsafe fn foo<T:Any>() -> TypeId { TypeId::of::<T>() }
pub fn foo<T: Any>() -> TypeId { TypeId::of::<T>() }
Loading