]> git.lizzy.rs Git - rust.git/commitdiff
Rewrite TypeId computation to not miss anything and work cross-crate.
authorEduard Burtescu <edy.burt@gmail.com>
Fri, 5 Aug 2016 21:50:13 +0000 (00:50 +0300)
committerEduard Burtescu <edy.burt@gmail.com>
Fri, 5 Aug 2016 21:50:13 +0000 (00:50 +0300)
src/librustc/ty/util.rs
src/librustc_trans/intrinsic.rs
src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs
src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs
src/test/run-pass/type-id-higher-rank-2.rs [new file with mode: 0644]
src/test/run-pass/type-id-higher-rank.rs
src/test/run-pass/typeid-intrinsic.rs

index fadf36471555b7f83a151bffd55c7c6fa020a915..d9ffe36ea47fbe3610cb927d346cdf4d7a3008d0 100644 (file)
@@ -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 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 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 @@ pub fn required_region_bounds(self,
 
     /// 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.
@@ -525,6 +391,143 @@ pub fn is_adt_dtorck(self, adt: ty::AdtDef) -> bool {
     }
 }
 
+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>,
index 2f27aed065d80f17b926d937a1d2ee473c2e1b4e..4980fad0cc37e676f3605bbcfdd667166e1681a7 100644 (file)
@@ -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);
index 388d3238d4248960c37b2a317d249fca570f5a69..42c0da6286bdca27f95692cd6b85c074ac6cc569 100644 (file)
 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>() }
index 3ad307fd3b507b6b3cf199b4abb946227c881beb..42c0da6286bdca27f95692cd6b85c074ac6cc569 100644 (file)
 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>() }
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 (file)
index 0000000..aead8bc
--- /dev/null
@@ -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 <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.
+
+// 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<Any> = Box::new(good as fn(&String) -> Foo);
+    a.downcast_ref::<fn(&String) -> 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<Any> = Box::new(Box::new(s) as Box<for<'a> AsStr<'a, 'a>>);
+    a.downcast_ref::<Box<for<'a> AsStr<'a, 'static>>>().map(|x| x.get())
+}
+
+fn main() {
+    assert_eq!(bad1(String::from("foo")), None);
+    assert_eq!(bad2(String::from("bar")), None);
+}
index c29fb5e86f51d54311b2db332b85c7a64910330d..827b05c0801e1eb39b5650defc365315f411b3d3 100644 (file)
@@ -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::<for<'a> fn(fn(&'a isize) -> &'a isize)>();
         let f = TypeId::of::<fn(for<'a> fn(&'a isize) -> &'a isize)>();
         assert!(e != f);
+
+        // Make sure lifetime parameters of items are not ignored.
+        let g = TypeId::of::<for<'a> fn(&'a Trait<'a>) -> Struct<'a>>();
+        let h = TypeId::of::<for<'a> fn(&'a Trait<'a>) -> Struct<'static>>();
+        let i = TypeId::of::<for<'a, 'b> fn(&'a Trait<'b>) -> Struct<'b>>();
+        assert!(g != h);
+        assert!(g != i);
+        assert!(h != i);
     }
     // Boxed unboxed closures
     {
index 4bd82baafeb100c32897ccf9584969dc98902d2d..e99a5f69af40f23c984301370f54297b6034de1f 100644 (file)
 struct Test;
 
 pub fn main() {
-    unsafe {
-        assert_eq!(TypeId::of::<other1::A>(), other1::id_A());
-        assert_eq!(TypeId::of::<other1::B>(), other1::id_B());
-        assert_eq!(TypeId::of::<other1::C>(), other1::id_C());
-        assert_eq!(TypeId::of::<other1::D>(), other1::id_D());
-        assert_eq!(TypeId::of::<other1::E>(), other1::id_E());
-        assert_eq!(TypeId::of::<other1::F>(), other1::id_F());
-        assert_eq!(TypeId::of::<other1::G>(), other1::id_G());
-        assert_eq!(TypeId::of::<other1::H>(), other1::id_H());
+    assert_eq!(TypeId::of::<other1::A>(), other1::id_A());
+    assert_eq!(TypeId::of::<other1::B>(), other1::id_B());
+    assert_eq!(TypeId::of::<other1::C>(), other1::id_C());
+    assert_eq!(TypeId::of::<other1::D>(), other1::id_D());
+    assert_eq!(TypeId::of::<other1::E>(), other1::id_E());
+    assert_eq!(TypeId::of::<other1::F>(), other1::id_F());
+    assert_eq!(TypeId::of::<other1::G>(), other1::id_G());
+    assert_eq!(TypeId::of::<other1::H>(), other1::id_H());
+    assert_eq!(TypeId::of::<other1::I>(), other1::id_I());
 
-        assert_eq!(TypeId::of::<other2::A>(), other2::id_A());
-        assert_eq!(TypeId::of::<other2::B>(), other2::id_B());
-        assert_eq!(TypeId::of::<other2::C>(), other2::id_C());
-        assert_eq!(TypeId::of::<other2::D>(), other2::id_D());
-        assert_eq!(TypeId::of::<other2::E>(), other2::id_E());
-        assert_eq!(TypeId::of::<other2::F>(), other2::id_F());
-        assert_eq!(TypeId::of::<other2::G>(), other2::id_G());
-        assert_eq!(TypeId::of::<other2::H>(), other2::id_H());
+    assert_eq!(TypeId::of::<other2::A>(), other2::id_A());
+    assert_eq!(TypeId::of::<other2::B>(), other2::id_B());
+    assert_eq!(TypeId::of::<other2::C>(), other2::id_C());
+    assert_eq!(TypeId::of::<other2::D>(), other2::id_D());
+    assert_eq!(TypeId::of::<other2::E>(), other2::id_E());
+    assert_eq!(TypeId::of::<other2::F>(), other2::id_F());
+    assert_eq!(TypeId::of::<other2::G>(), other2::id_G());
+    assert_eq!(TypeId::of::<other2::H>(), other2::id_H());
+    assert_eq!(TypeId::of::<other1::I>(), 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::<isize>(), other2::foo::<isize>());
-        assert_eq!(TypeId::of::<isize>(), other1::foo::<isize>());
-        assert_eq!(other2::foo::<isize>(), other1::foo::<isize>());
-        assert_eq!(TypeId::of::<A>(), other2::foo::<A>());
-        assert_eq!(TypeId::of::<A>(), other1::foo::<A>());
-        assert_eq!(other2::foo::<A>(), other1::foo::<A>());
-    }
+    assert_eq!(TypeId::of::<isize>(), other2::foo::<isize>());
+    assert_eq!(TypeId::of::<isize>(), other1::foo::<isize>());
+    assert_eq!(other2::foo::<isize>(), other1::foo::<isize>());
+    assert_eq!(TypeId::of::<A>(), other2::foo::<A>());
+    assert_eq!(TypeId::of::<A>(), other1::foo::<A>());
+    assert_eq!(other2::foo::<A>(), other1::foo::<A>());
 
     // sanity test of TypeId
     let (a, b, c) = (TypeId::of::<usize>(), TypeId::of::<&'static str>(),