Add support to the hashing infrastructure for automatically hashing both
authorChandler Carruth <chandlerc@gmail.com>
Wed, 7 Mar 2012 09:32:32 +0000 (09:32 +0000)
committerChandler Carruth <chandlerc@gmail.com>
Wed, 7 Mar 2012 09:32:32 +0000 (09:32 +0000)
integral and enumeration types. This is accomplished with a bit of
template type trait magic. Thanks to Richard Smith for the core idea
here to detect viable types by detecting the set of types which can be
default constructed in a template parameter.

This is used (in conjunction with a system for detecting nullptr_t
should it exist) to provide an is_integral_or_enum type trait that
doesn't need a whitelist or direct compiler support.

With this, the hashing is extended to the more general facility. This
will be used in a subsequent commit to hashing more things, but I wanted
to make sure the type trait magic went through the build bots separately
in case other compilers don't like this formulation.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@152217 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/ADT/Hashing.h
include/llvm/Support/type_traits.h
unittests/ADT/HashingTest.cpp

index 5efaa72705e3311c88c39d00921020dbd354571b..b2bd72e7021b8a7b69c9fe8f05fcbf9e10cceaa0 100644 (file)
@@ -113,7 +113,7 @@ public:
 /// differing argument types even if they would implicit promote to a common
 /// type without changing the value.
 template <typename T>
-typename enable_if<is_integral<T>, hash_code>::type hash_value(T value);
+typename enable_if<is_integral_or_enum<T>, hash_code>::type hash_value(T value);
 
 /// \brief Compute a hash_code for a pointer's address.
 ///
@@ -349,14 +349,15 @@ inline size_t get_execution_seed() {
 /// reading the underlying data. It is false if values of this type must
 /// first be passed to hash_value, and the resulting hash_codes combined.
 //
-// FIXME: We want to replace is_integral and is_pointer here with a predicate
-// which asserts that comparing the underlying storage of two values of the
-// type for equality is equivalent to comparing the two values for equality.
-// For all the platforms we care about, this holds for integers and pointers,
-// but there are platforms where it doesn't and we would like to support
-// user-defined types which happen to satisfy this property.
+// FIXME: We want to replace is_integral_or_enum and is_pointer here with
+// a predicate which asserts that comparing the underlying storage of two
+// values of the type for equality is equivalent to comparing the two values
+// for equality. For all the platforms we care about, this holds for integers
+// and pointers, but there are platforms where it doesn't and we would like to
+// support user-defined types which happen to satisfy this property.
 template <typename T> struct is_hashable_data
-  : integral_constant<bool, ((is_integral<T>::value || is_pointer<T>::value) &&
+  : integral_constant<bool, ((is_integral_or_enum<T>::value ||
+                              is_pointer<T>::value) &&
                              64 % sizeof(T) == 0)> {};
 
 // Special case std::pair to detect when both types are viable and when there
@@ -732,7 +733,8 @@ inline hash_code hash_integer_value(uint64_t value) {
 // Declared and documented above, but defined here so that any of the hashing
 // infrastructure is available.
 template <typename T>
-typename enable_if<is_integral<T>, hash_code>::type hash_value(T value) {
+typename enable_if<is_integral_or_enum<T>, hash_code>::type
+hash_value(T value) {
   return ::llvm::hashing::detail::hash_integer_value(value);
 }
 
index 85d90b17f17b5f4cc79ed67f561f1f5f7d3dbf72..a76344c098fe179fde5353f1aa03d0b44c7c7c95 100644 (file)
@@ -121,12 +121,42 @@ template <> struct is_integral_impl<unsigned long long> : true_type {};
 template <typename T>
 struct is_integral : is_integral_impl<T> {};
 
+/// \brief Metafunction that determines whether the given type is either an
+/// integral type or an enumeration type.
+///
+/// Note that this accepts potentially more integral types than we whitelist
+/// above for is_integral, it should accept essentially anything the compiler
+/// believes is an integral type.
+template <typename T> class is_integral_or_enum {
+
+  // Form a return type that can only be instantiated with an integral or enum
+  // types (or with nullptr_t in C++11).
+  template <typename U, U u = U()> struct check1_return_type { char c[2]; };
+  template <typename U> static check1_return_type<U> checker1(U*);
+  static char checker1(...);
+
+  // Form a return type that can only be instantiated with nullptr_t in C++11
+  // mode. It's harmless in C++98 mode, but this allows us to filter nullptr_t
+  // when building in C++11 mode without having to detect that mode for each
+  // different compiler.
+  struct nonce {};
+  template <typename U, nonce* u = U()>
+  struct check2_return_type { char c[2]; };
+  template <typename U> static check2_return_type<U> checker2(U*);
+  static char checker2(...);
+
+public:
+  enum {
+    value = (sizeof(char) != sizeof(checker1((T*)0)) &&
+             sizeof(char) == sizeof(checker2((T*)0)))
+  };
+};
+
 /// \brief Metafunction that determines whether the given type is a pointer
 /// type.
 template <typename T> struct is_pointer : false_type {};
 template <typename T> struct is_pointer<T*> : true_type {};
 
-  
 // enable_if_c - Enable/disable a template based on a metafunction
 template<bool Cond, typename T = void>
 struct enable_if_c {
index f5d6aed5b9931c45e804e4891e429727cf051d36..b148f144513c2d967338146b11ed9018fec9be59 100644 (file)
@@ -51,6 +51,10 @@ using namespace llvm;
 
 namespace {
 
+enum TestEnumeration {
+  TE_Foo = 42,
+  TE_Bar = 43
+};
 
 TEST(HashingTest, HashValueBasicTest) {
   int x = 42, y = 43, c = 'x';
@@ -61,7 +65,9 @@ TEST(HashingTest, HashValueBasicTest) {
   const volatile int cvi = 71;
   uintptr_t addr = reinterpret_cast<uintptr_t>(&y);
   EXPECT_EQ(hash_value(42), hash_value(x));
+  EXPECT_EQ(hash_value(42), hash_value(TE_Foo));
   EXPECT_NE(hash_value(42), hash_value(y));
+  EXPECT_NE(hash_value(42), hash_value(TE_Bar));
   EXPECT_NE(hash_value(42), hash_value(p));
   EXPECT_EQ(hash_value(71), hash_value(i));
   EXPECT_EQ(hash_value(71), hash_value(ci));