Skip to content

Commit

Permalink
Rewrite TypeId computation to not miss anything and work cross-crate.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Aug 5, 2016
1 parent b30eff7 commit d42da7b
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 191 deletions.
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)
}

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

0 comments on commit d42da7b

Please sign in to comment.