From d42da7b8f36fe087b5fe2cb08c136ffd62f73e3d Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Sat, 6 Aug 2016 00:50:13 +0300 Subject: [PATCH] Rewrite TypeId computation to not miss anything and work cross-crate. --- src/librustc/ty/util.rs | 289 +++++++++--------- src/librustc_trans/intrinsic.rs | 4 +- .../auxiliary/typeid-intrinsic-aux1.rs | 20 +- .../auxiliary/typeid-intrinsic-aux2.rs | 20 +- src/test/run-pass/type-id-higher-rank-2.rs | 40 +++ src/test/run-pass/type-id-higher-rank.rs | 11 + src/test/run-pass/typeid-intrinsic.rs | 55 ++-- 7 files changed, 248 insertions(+), 191 deletions(-) create mode 100644 src/test/run-pass/type-id-higher-rank-2.rs diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index fadf36471555b..d9ffe36ea47fb 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -10,7 +10,6 @@ //! 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; @@ -18,6 +17,7 @@ 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::*; @@ -25,6 +25,7 @@ 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; @@ -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>| { - 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. @@ -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(&mut self, x: T) { + x.hash(&mut self.state); + } + + fn hash_discriminant_u8(&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::(0); + } + ty::ReLateBound(db, ty::BrAnon(i)) => { + assert!(db.depth > 0); + self.hash::(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>(&mut self, x: &ty::Binder) -> 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>, diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 2f27aed065d80..4980fad0cc37e 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -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); diff --git a/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs b/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs index 388d3238d4248..42c0da6286bdc 100644 --- a/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs +++ b/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs @@ -21,14 +21,16 @@ pub struct E(Result<&'static str, isize>); pub type F = Option; pub type G = usize; pub type H = &'static str; +pub type I = Box; -pub unsafe fn id_A() -> TypeId { TypeId::of::() } -pub unsafe fn id_B() -> TypeId { TypeId::of::() } -pub unsafe fn id_C() -> TypeId { TypeId::of::() } -pub unsafe fn id_D() -> TypeId { TypeId::of::() } -pub unsafe fn id_E() -> TypeId { TypeId::of::() } -pub unsafe fn id_F() -> TypeId { TypeId::of::() } -pub unsafe fn id_G() -> TypeId { TypeId::of::() } -pub unsafe fn id_H() -> TypeId { TypeId::of::() } +pub fn id_A() -> TypeId { TypeId::of::() } +pub fn id_B() -> TypeId { TypeId::of::() } +pub fn id_C() -> TypeId { TypeId::of::() } +pub fn id_D() -> TypeId { TypeId::of::() } +pub fn id_E() -> TypeId { TypeId::of::() } +pub fn id_F() -> TypeId { TypeId::of::() } +pub fn id_G() -> TypeId { TypeId::of::() } +pub fn id_H() -> TypeId { TypeId::of::() } +pub fn id_I() -> TypeId { TypeId::of::() } -pub unsafe fn foo() -> TypeId { TypeId::of::() } +pub fn foo() -> TypeId { TypeId::of::() } diff --git a/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs b/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs index 3ad307fd3b507..42c0da6286bdc 100644 --- a/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs +++ b/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs @@ -21,14 +21,16 @@ pub struct E(Result<&'static str, isize>); pub type F = Option; pub type G = usize; pub type H = &'static str; +pub type I = Box; -pub unsafe fn id_A() -> TypeId { TypeId::of::() } -pub unsafe fn id_B() -> TypeId { TypeId::of::() } -pub unsafe fn id_C() -> TypeId { TypeId::of::() } -pub unsafe fn id_D() -> TypeId { TypeId::of::() } -pub unsafe fn id_E() -> TypeId { TypeId::of::() } -pub unsafe fn id_F() -> TypeId { TypeId::of::() } -pub unsafe fn id_G() -> TypeId { TypeId::of::() } -pub unsafe fn id_H() -> TypeId { TypeId::of::() } +pub fn id_A() -> TypeId { TypeId::of::() } +pub fn id_B() -> TypeId { TypeId::of::() } +pub fn id_C() -> TypeId { TypeId::of::() } +pub fn id_D() -> TypeId { TypeId::of::() } +pub fn id_E() -> TypeId { TypeId::of::() } +pub fn id_F() -> TypeId { TypeId::of::() } +pub fn id_G() -> TypeId { TypeId::of::() } +pub fn id_H() -> TypeId { TypeId::of::() } +pub fn id_I() -> TypeId { TypeId::of::() } -pub unsafe fn foo() -> TypeId { TypeId::of::() } +pub fn foo() -> TypeId { TypeId::of::() } diff --git a/src/test/run-pass/type-id-higher-rank-2.rs b/src/test/run-pass/type-id-higher-rank-2.rs new file mode 100644 index 0000000000000..aead8bc264d7e --- /dev/null +++ b/src/test/run-pass/type-id-higher-rank-2.rs @@ -0,0 +1,40 @@ +// 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. + +// Test that we can't ignore lifetimes by going through Any. + +use std::any::Any; + +struct Foo<'a>(&'a str); + +fn good(s: &String) -> Foo { Foo(s) } + +fn bad1(s: String) -> Option<&'static str> { + let a: Box = Box::new(good as fn(&String) -> Foo); + a.downcast_ref:: Foo<'static>>().map(|f| f(&s).0) +} + +trait AsStr<'a, 'b> { + fn get(&'a self) -> &'b str; +} + +impl<'a> AsStr<'a, 'a> for String { + fn get(&'a self) -> &'a str { self } +} + +fn bad2(s: String) -> Option<&'static str> { + let a: Box = Box::new(Box::new(s) as Box AsStr<'a, 'a>>); + a.downcast_ref:: AsStr<'a, 'static>>>().map(|x| x.get()) +} + +fn main() { + assert_eq!(bad1(String::from("foo")), None); + assert_eq!(bad2(String::from("bar")), None); +} diff --git a/src/test/run-pass/type-id-higher-rank.rs b/src/test/run-pass/type-id-higher-rank.rs index c29fb5e86f51d..827b05c0801e1 100644 --- a/src/test/run-pass/type-id-higher-rank.rs +++ b/src/test/run-pass/type-id-higher-rank.rs @@ -16,6 +16,9 @@ use std::any::{Any, TypeId}; +struct Struct<'a>(&'a ()); +trait Trait<'a> {} + fn main() { // Bare fns { @@ -34,6 +37,14 @@ fn main() { let e = TypeId::of:: fn(fn(&'a isize) -> &'a isize)>(); let f = TypeId::of:: fn(&'a isize) -> &'a isize)>(); assert!(e != f); + + // Make sure lifetime parameters of items are not ignored. + let g = TypeId::of:: fn(&'a Trait<'a>) -> Struct<'a>>(); + let h = TypeId::of:: fn(&'a Trait<'a>) -> Struct<'static>>(); + let i = TypeId::of:: fn(&'a Trait<'b>) -> Struct<'b>>(); + assert!(g != h); + assert!(g != i); + assert!(h != i); } // Boxed unboxed closures { diff --git a/src/test/run-pass/typeid-intrinsic.rs b/src/test/run-pass/typeid-intrinsic.rs index 4bd82baafeb10..e99a5f69af40f 100644 --- a/src/test/run-pass/typeid-intrinsic.rs +++ b/src/test/run-pass/typeid-intrinsic.rs @@ -23,36 +23,37 @@ struct A; struct Test; pub fn main() { - unsafe { - assert_eq!(TypeId::of::(), other1::id_A()); - assert_eq!(TypeId::of::(), other1::id_B()); - assert_eq!(TypeId::of::(), other1::id_C()); - assert_eq!(TypeId::of::(), other1::id_D()); - assert_eq!(TypeId::of::(), other1::id_E()); - assert_eq!(TypeId::of::(), other1::id_F()); - assert_eq!(TypeId::of::(), other1::id_G()); - assert_eq!(TypeId::of::(), other1::id_H()); + assert_eq!(TypeId::of::(), other1::id_A()); + assert_eq!(TypeId::of::(), other1::id_B()); + assert_eq!(TypeId::of::(), other1::id_C()); + assert_eq!(TypeId::of::(), other1::id_D()); + assert_eq!(TypeId::of::(), other1::id_E()); + assert_eq!(TypeId::of::(), other1::id_F()); + assert_eq!(TypeId::of::(), other1::id_G()); + assert_eq!(TypeId::of::(), other1::id_H()); + assert_eq!(TypeId::of::(), other1::id_I()); - assert_eq!(TypeId::of::(), other2::id_A()); - assert_eq!(TypeId::of::(), other2::id_B()); - assert_eq!(TypeId::of::(), other2::id_C()); - assert_eq!(TypeId::of::(), other2::id_D()); - assert_eq!(TypeId::of::(), other2::id_E()); - assert_eq!(TypeId::of::(), other2::id_F()); - assert_eq!(TypeId::of::(), other2::id_G()); - assert_eq!(TypeId::of::(), other2::id_H()); + assert_eq!(TypeId::of::(), other2::id_A()); + assert_eq!(TypeId::of::(), other2::id_B()); + assert_eq!(TypeId::of::(), other2::id_C()); + assert_eq!(TypeId::of::(), other2::id_D()); + assert_eq!(TypeId::of::(), other2::id_E()); + assert_eq!(TypeId::of::(), other2::id_F()); + assert_eq!(TypeId::of::(), other2::id_G()); + assert_eq!(TypeId::of::(), other2::id_H()); + assert_eq!(TypeId::of::(), other2::id_I()); - assert_eq!(other1::id_F(), other2::id_F()); - assert_eq!(other1::id_G(), other2::id_G()); - assert_eq!(other1::id_H(), other2::id_H()); + assert_eq!(other1::id_F(), other2::id_F()); + assert_eq!(other1::id_G(), other2::id_G()); + assert_eq!(other1::id_H(), other2::id_H()); + assert_eq!(other1::id_I(), other2::id_I()); - assert_eq!(TypeId::of::(), other2::foo::()); - assert_eq!(TypeId::of::(), other1::foo::()); - assert_eq!(other2::foo::(), other1::foo::()); - assert_eq!(TypeId::of::(), other2::foo::()); - assert_eq!(TypeId::of::(), other1::foo::()); - assert_eq!(other2::foo::(), other1::foo::()); - } + assert_eq!(TypeId::of::(), other2::foo::()); + assert_eq!(TypeId::of::(), other1::foo::()); + assert_eq!(other2::foo::(), other1::foo::()); + assert_eq!(TypeId::of::(), other2::foo::()); + assert_eq!(TypeId::of::(), other1::foo::()); + assert_eq!(other2::foo::(), other1::foo::()); // sanity test of TypeId let (a, b, c) = (TypeId::of::(), TypeId::of::<&'static str>(),