hashtable_key_context_test.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "common/hashtable_key_context.h"
  5. #include <gmock/gmock.h>
  6. #include <gtest/gtest.h>
  7. namespace Carbon {
  8. namespace {
  9. using ::testing::Eq;
  10. using ::testing::Ne;
  11. struct DefaultEq {
  12. int x, y;
  13. friend auto operator==(const DefaultEq& lhs, const DefaultEq& rhs)
  14. -> bool = default;
  15. };
  16. struct CustomEq {
  17. int x, y;
  18. friend auto operator==(const CustomEq& lhs, const CustomEq& rhs) -> bool {
  19. return lhs.x == rhs.x && lhs.y == rhs.y;
  20. }
  21. };
  22. struct CustomExtEq {
  23. int x, y;
  24. friend auto CarbonHashtableEq(const CustomExtEq& lhs, const CustomExtEq& rhs)
  25. -> bool {
  26. return lhs.x == rhs.x && lhs.y == rhs.y;
  27. }
  28. };
  29. TEST(HashtableKeyContextTest, HashtableEq) {
  30. EXPECT_TRUE(HashtableEq(0, 0));
  31. EXPECT_FALSE(HashtableEq(1, 0));
  32. EXPECT_FALSE(HashtableEq(0, 1));
  33. EXPECT_FALSE(HashtableEq(1234, 5678));
  34. EXPECT_TRUE(HashtableEq(5678, 5678));
  35. EXPECT_TRUE(
  36. HashtableEq(DefaultEq{.x = 0, .y = 0}, DefaultEq{.x = 0, .y = 0}));
  37. EXPECT_FALSE(
  38. HashtableEq(DefaultEq{.x = 1, .y = 2}, DefaultEq{.x = 3, .y = 4}));
  39. EXPECT_TRUE(HashtableEq(CustomEq{.x = 0, .y = 0}, CustomEq{.x = 0, .y = 0}));
  40. EXPECT_FALSE(HashtableEq(CustomEq{.x = 1, .y = 2}, CustomEq{.x = 3, .y = 4}));
  41. EXPECT_TRUE(
  42. HashtableEq(CustomExtEq{.x = 0, .y = 0}, CustomExtEq{.x = 0, .y = 0}));
  43. EXPECT_FALSE(
  44. HashtableEq(CustomExtEq{.x = 1, .y = 2}, CustomExtEq{.x = 3, .y = 4}));
  45. }
  46. TEST(HashtableKeyContextTest, HashtableEqAPInt) {
  47. // Hashtable equality doesn't assert on mismatched bit width, it includes the
  48. // bit width in the comparison.
  49. llvm::APInt one_64(/*numBits=*/64, /*val=*/1);
  50. llvm::APInt two_64(/*numBits=*/64, /*val=*/2);
  51. llvm::APInt one_128(/*numBits=*/128, /*val=*/1);
  52. llvm::APInt two_128(/*numBits=*/128, /*val=*/2);
  53. EXPECT_TRUE(HashtableEq(one_64, one_64));
  54. EXPECT_FALSE(HashtableEq(one_64, one_128));
  55. EXPECT_TRUE(HashtableEq(two_128, two_128));
  56. EXPECT_FALSE(HashtableEq(two_64, two_128));
  57. EXPECT_FALSE(HashtableEq(one_64, two_64));
  58. EXPECT_FALSE(HashtableEq(one_64, two_128));
  59. EXPECT_FALSE(HashtableEq(one_128, two_128));
  60. EXPECT_FALSE(HashtableEq(one_128, two_64));
  61. }
  62. TEST(HashtableKeyContextTest, HashtableEqAPFloat) {
  63. // Hashtable equality for `APFloat` uses a bitwise comparison. This
  64. // differentiates between various things that would otherwise not make sense:
  65. // - Different floating point semantics
  66. // - `-0.0` and `0.0`
  67. //
  68. // It also allows NaNs to be compared meaningfully.
  69. llvm::APFloat zero_float =
  70. llvm::APFloat::getZero(llvm::APFloat::IEEEsingle());
  71. llvm::APFloat neg_zero_float =
  72. llvm::APFloat::getZero(llvm::APFloat::IEEEsingle(), /*Negative=*/true);
  73. llvm::APFloat zero_double =
  74. llvm::APFloat::getZero(llvm::APFloat::IEEEdouble());
  75. llvm::APFloat zero_bfloat = llvm::APFloat::getZero(llvm::APFloat::BFloat());
  76. llvm::APFloat one_float = llvm::APFloat::getOne(llvm::APFloat::IEEEsingle());
  77. llvm::APFloat inf_float = llvm::APFloat::getInf(llvm::APFloat::IEEEsingle());
  78. llvm::APFloat nan_0_float = llvm::APFloat::getNaN(
  79. llvm::APFloat::IEEEsingle(), /*Negative=*/false, /*payload=*/0);
  80. llvm::APFloat nan_42_float = llvm::APFloat::getNaN(
  81. llvm::APFloat::IEEEsingle(), /*Negative=*/false, /*payload=*/42);
  82. // Boring cases.
  83. EXPECT_TRUE(HashtableEq(zero_float, zero_float));
  84. EXPECT_FALSE(HashtableEq(zero_float, one_float));
  85. EXPECT_TRUE(HashtableEq(inf_float, inf_float));
  86. EXPECT_FALSE(HashtableEq(inf_float, one_float));
  87. // Confirm a case where we expect `==` to work but produce a different result.
  88. ASSERT_TRUE(zero_float == neg_zero_float);
  89. EXPECT_FALSE(HashtableEq(zero_float, neg_zero_float));
  90. // Now work through less reasonable things outside of a hashtable such as
  91. // mixing semantics and NaNs.
  92. EXPECT_FALSE(HashtableEq(zero_float, zero_double));
  93. EXPECT_FALSE(HashtableEq(zero_float, zero_bfloat));
  94. EXPECT_FALSE(HashtableEq(zero_float, nan_0_float));
  95. EXPECT_FALSE(HashtableEq(zero_float, nan_42_float));
  96. EXPECT_FALSE(HashtableEq(nan_0_float, nan_42_float));
  97. }
  98. struct CustomHash {
  99. int x;
  100. friend auto CarbonHashValue(const CustomHash& value, uint64_t seed)
  101. -> HashCode {
  102. return HashValue(value.x + 42, seed);
  103. }
  104. };
  105. TEST(HashtableKeyContextTest, DefaultKeyContext) {
  106. // Make sure the default context dispatches appropriately, including for
  107. // interesting types. We don't cover all the cases here and use the direct
  108. // tests of `HashtableEq` for that.
  109. DefaultKeyContext context;
  110. EXPECT_FALSE(context.KeyEq(1234, 5678));
  111. EXPECT_TRUE(context.KeyEq(5678, 5678));
  112. EXPECT_TRUE(context.KeyEq(DefaultEq{0, 0}, DefaultEq{0, 0}));
  113. EXPECT_FALSE(context.KeyEq(DefaultEq{1, 2}, DefaultEq{3, 4}));
  114. EXPECT_TRUE(context.KeyEq(CustomEq{0, 0}, CustomEq{0, 0}));
  115. EXPECT_FALSE(context.KeyEq(CustomEq{1, 2}, CustomEq{3, 4}));
  116. EXPECT_TRUE(context.KeyEq(CustomExtEq{0, 0}, CustomExtEq{0, 0}));
  117. EXPECT_FALSE(context.KeyEq(CustomExtEq{1, 2}, CustomExtEq{3, 4}));
  118. llvm::APInt one_64(/*numBits=*/64, /*val=*/1);
  119. llvm::APInt one_128(/*numBits=*/128, /*val=*/1);
  120. EXPECT_TRUE(HashtableEq(one_64, one_64));
  121. EXPECT_FALSE(HashtableEq(one_64, one_128));
  122. llvm::APFloat zero_float =
  123. llvm::APFloat::getZero(llvm::APFloat::IEEEsingle());
  124. llvm::APFloat neg_zero_float =
  125. llvm::APFloat::getZero(llvm::APFloat::IEEEsingle(), /*Negative=*/true);
  126. EXPECT_TRUE(HashtableEq(zero_float, zero_float));
  127. EXPECT_FALSE(HashtableEq(zero_float, neg_zero_float));
  128. // Also check hash dispatching.
  129. uint64_t seed = 1234;
  130. EXPECT_THAT(context.HashKey(42, seed), Eq(HashValue(42, seed)));
  131. EXPECT_THAT(context.HashKey(CustomHash{.x = 1234}, seed),
  132. Eq(HashValue(CustomHash{.x = 1234}, seed)));
  133. EXPECT_THAT(context.HashKey(one_64, seed), Eq(HashValue(one_64, seed)));
  134. EXPECT_THAT(context.HashKey(one_128, seed), Eq(HashValue(one_128, seed)));
  135. EXPECT_THAT(context.HashKey(one_64, seed),
  136. Ne(context.HashKey(one_128, seed)));
  137. EXPECT_THAT(context.HashKey(zero_float, seed),
  138. Eq(HashValue(zero_float, seed)));
  139. EXPECT_THAT(context.HashKey(neg_zero_float, seed),
  140. Eq(HashValue(neg_zero_float, seed)));
  141. EXPECT_THAT(context.HashKey(zero_float, seed),
  142. Ne(context.HashKey(neg_zero_float, seed)));
  143. }
  144. struct TestTranslatingKeyContext
  145. : TranslatingKeyContext<TestTranslatingKeyContext> {
  146. auto TranslateKey(int index) const -> const llvm::APInt& {
  147. return array[index];
  148. }
  149. llvm::ArrayRef<llvm::APInt> array;
  150. };
  151. TEST(HashtableKeyContextTest, TranslatingKeyContext) {
  152. llvm::APInt one_64(/*numBits=*/64, /*val=*/1);
  153. llvm::APInt two_64(/*numBits=*/64, /*val=*/2);
  154. llvm::APInt one_128(/*numBits=*/128, /*val=*/1);
  155. llvm::APInt two_128(/*numBits=*/128, /*val=*/2);
  156. // An array of values, including some duplicates.
  157. llvm::SmallVector<llvm::APInt> values = {one_64, two_64, one_128,
  158. two_128, one_64, one_64};
  159. TestTranslatingKeyContext context = {.array = values};
  160. uint64_t seed = 1234;
  161. EXPECT_THAT(context.HashKey(0, seed), Eq(HashValue(one_64, seed)));
  162. EXPECT_THAT(context.HashKey(1, seed), Eq(HashValue(two_64, seed)));
  163. EXPECT_THAT(context.HashKey(2, seed), Eq(HashValue(one_128, seed)));
  164. EXPECT_THAT(context.HashKey(3, seed), Eq(HashValue(two_128, seed)));
  165. EXPECT_THAT(context.HashKey(4, seed), Eq(HashValue(one_64, seed)));
  166. EXPECT_THAT(context.HashKey(5, seed), Eq(HashValue(one_64, seed)));
  167. EXPECT_TRUE(context.KeyEq(one_64, 0));
  168. EXPECT_TRUE(context.KeyEq(one_64, 4));
  169. EXPECT_TRUE(context.KeyEq(one_64, 5));
  170. EXPECT_TRUE(context.KeyEq(0, one_64));
  171. EXPECT_TRUE(context.KeyEq(0, 0));
  172. EXPECT_TRUE(context.KeyEq(0, 4));
  173. EXPECT_TRUE(context.KeyEq(4, 5));
  174. EXPECT_FALSE(context.KeyEq(one_64, 1));
  175. EXPECT_FALSE(context.KeyEq(one_64, 2));
  176. EXPECT_FALSE(context.KeyEq(one_64, 3));
  177. EXPECT_FALSE(context.KeyEq(1, one_64));
  178. EXPECT_FALSE(context.KeyEq(2, one_64));
  179. EXPECT_FALSE(context.KeyEq(3, one_64));
  180. EXPECT_FALSE(context.KeyEq(0, 1));
  181. EXPECT_FALSE(context.KeyEq(0, 2));
  182. EXPECT_FALSE(context.KeyEq(4, 3));
  183. }
  184. } // namespace
  185. } // namespace Carbon