Skip to content

Commit f8c6135

Browse files
committed
Reject out-of-bounds enum sentinels in DenseMap/DenseSet.
This makes the bug in PR llvm#125556 which was fixed by dc87a14 into a compile-time error.
1 parent ce52f9c commit f8c6135

File tree

1 file changed

+45
-33
lines changed

1 file changed

+45
-33
lines changed

llvm/include/llvm/ADT/DenseMapInfo.h

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ inline unsigned combineHashValue(unsigned a, unsigned b) {
5151
/// just be `void`.
5252
template<typename T, typename Enable = void>
5353
struct DenseMapInfo {
54-
//static inline T getEmptyKey();
55-
//static inline T getTombstoneKey();
54+
//static constexpr T getEmptyKey();
55+
//static constexpr T getTombstoneKey();
5656
//static unsigned getHashValue(const T &Val);
5757
//static bool isEqual(const T &LHS, const T &RHS);
5858
};
@@ -70,13 +70,13 @@ struct DenseMapInfo<T*> {
7070
// "Log2MaxAlign bits of alignment");
7171
static constexpr uintptr_t Log2MaxAlign = 12;
7272

73-
static inline T* getEmptyKey() {
73+
static constexpr T* getEmptyKey() {
7474
uintptr_t Val = static_cast<uintptr_t>(-1);
7575
Val <<= Log2MaxAlign;
7676
return reinterpret_cast<T*>(Val);
7777
}
7878

79-
static inline T* getTombstoneKey() {
79+
static constexpr T* getTombstoneKey() {
8080
uintptr_t Val = static_cast<uintptr_t>(-2);
8181
Val <<= Log2MaxAlign;
8282
return reinterpret_cast<T*>(Val);
@@ -92,8 +92,8 @@ struct DenseMapInfo<T*> {
9292

9393
// Provide DenseMapInfo for chars.
9494
template<> struct DenseMapInfo<char> {
95-
static inline char getEmptyKey() { return ~0; }
96-
static inline char getTombstoneKey() { return ~0 - 1; }
95+
static constexpr char getEmptyKey() { return ~0; }
96+
static constexpr char getTombstoneKey() { return ~0 - 1; }
9797
static unsigned getHashValue(const char& Val) { return Val * 37U; }
9898

9999
static bool isEqual(const char &LHS, const char &RHS) {
@@ -103,8 +103,8 @@ template<> struct DenseMapInfo<char> {
103103

104104
// Provide DenseMapInfo for unsigned chars.
105105
template <> struct DenseMapInfo<unsigned char> {
106-
static inline unsigned char getEmptyKey() { return ~0; }
107-
static inline unsigned char getTombstoneKey() { return ~0 - 1; }
106+
static constexpr unsigned char getEmptyKey() { return ~0; }
107+
static constexpr unsigned char getTombstoneKey() { return ~0 - 1; }
108108
static unsigned getHashValue(const unsigned char &Val) { return Val * 37U; }
109109

110110
static bool isEqual(const unsigned char &LHS, const unsigned char &RHS) {
@@ -114,8 +114,8 @@ template <> struct DenseMapInfo<unsigned char> {
114114

115115
// Provide DenseMapInfo for unsigned shorts.
116116
template <> struct DenseMapInfo<unsigned short> {
117-
static inline unsigned short getEmptyKey() { return 0xFFFF; }
118-
static inline unsigned short getTombstoneKey() { return 0xFFFF - 1; }
117+
static constexpr unsigned short getEmptyKey() { return 0xFFFF; }
118+
static constexpr unsigned short getTombstoneKey() { return 0xFFFF - 1; }
119119
static unsigned getHashValue(const unsigned short &Val) { return Val * 37U; }
120120

121121
static bool isEqual(const unsigned short &LHS, const unsigned short &RHS) {
@@ -125,8 +125,8 @@ template <> struct DenseMapInfo<unsigned short> {
125125

126126
// Provide DenseMapInfo for unsigned ints.
127127
template<> struct DenseMapInfo<unsigned> {
128-
static inline unsigned getEmptyKey() { return ~0U; }
129-
static inline unsigned getTombstoneKey() { return ~0U - 1; }
128+
static constexpr unsigned getEmptyKey() { return ~0U; }
129+
static constexpr unsigned getTombstoneKey() { return ~0U - 1; }
130130
static unsigned getHashValue(const unsigned& Val) { return Val * 37U; }
131131

132132
static bool isEqual(const unsigned& LHS, const unsigned& RHS) {
@@ -136,8 +136,8 @@ template<> struct DenseMapInfo<unsigned> {
136136

137137
// Provide DenseMapInfo for unsigned longs.
138138
template<> struct DenseMapInfo<unsigned long> {
139-
static inline unsigned long getEmptyKey() { return ~0UL; }
140-
static inline unsigned long getTombstoneKey() { return ~0UL - 1L; }
139+
static constexpr unsigned long getEmptyKey() { return ~0UL; }
140+
static constexpr unsigned long getTombstoneKey() { return ~0UL - 1L; }
141141

142142
static unsigned getHashValue(const unsigned long& Val) {
143143
if constexpr (sizeof(Val) == 4)
@@ -153,8 +153,8 @@ template<> struct DenseMapInfo<unsigned long> {
153153

154154
// Provide DenseMapInfo for unsigned long longs.
155155
template<> struct DenseMapInfo<unsigned long long> {
156-
static inline unsigned long long getEmptyKey() { return ~0ULL; }
157-
static inline unsigned long long getTombstoneKey() { return ~0ULL - 1ULL; }
156+
static constexpr unsigned long long getEmptyKey() { return ~0ULL; }
157+
static constexpr unsigned long long getTombstoneKey() { return ~0ULL - 1ULL; }
158158

159159
static unsigned getHashValue(const unsigned long long& Val) {
160160
return densemap::detail::mix(Val);
@@ -168,16 +168,16 @@ template<> struct DenseMapInfo<unsigned long long> {
168168

169169
// Provide DenseMapInfo for shorts.
170170
template <> struct DenseMapInfo<short> {
171-
static inline short getEmptyKey() { return 0x7FFF; }
172-
static inline short getTombstoneKey() { return -0x7FFF - 1; }
171+
static constexpr short getEmptyKey() { return 0x7FFF; }
172+
static constexpr short getTombstoneKey() { return -0x7FFF - 1; }
173173
static unsigned getHashValue(const short &Val) { return Val * 37U; }
174174
static bool isEqual(const short &LHS, const short &RHS) { return LHS == RHS; }
175175
};
176176

177177
// Provide DenseMapInfo for ints.
178178
template<> struct DenseMapInfo<int> {
179-
static inline int getEmptyKey() { return 0x7fffffff; }
180-
static inline int getTombstoneKey() { return -0x7fffffff - 1; }
179+
static constexpr int getEmptyKey() { return 0x7fffffff; }
180+
static constexpr int getTombstoneKey() { return -0x7fffffff - 1; }
181181
static unsigned getHashValue(const int& Val) { return (unsigned)(Val * 37U); }
182182

183183
static bool isEqual(const int& LHS, const int& RHS) {
@@ -187,11 +187,11 @@ template<> struct DenseMapInfo<int> {
187187

188188
// Provide DenseMapInfo for longs.
189189
template<> struct DenseMapInfo<long> {
190-
static inline long getEmptyKey() {
190+
static constexpr long getEmptyKey() {
191191
return (1UL << (sizeof(long) * 8 - 1)) - 1UL;
192192
}
193193

194-
static inline long getTombstoneKey() { return getEmptyKey() - 1L; }
194+
static constexpr long getTombstoneKey() { return getEmptyKey() - 1L; }
195195

196196
static unsigned getHashValue(const long& Val) {
197197
return (unsigned)(Val * 37UL);
@@ -204,8 +204,8 @@ template<> struct DenseMapInfo<long> {
204204

205205
// Provide DenseMapInfo for long longs.
206206
template<> struct DenseMapInfo<long long> {
207-
static inline long long getEmptyKey() { return 0x7fffffffffffffffLL; }
208-
static inline long long getTombstoneKey() { return -0x7fffffffffffffffLL-1; }
207+
static constexpr long long getEmptyKey() { return 0x7fffffffffffffffLL; }
208+
static constexpr long long getTombstoneKey() { return -0x7fffffffffffffffLL-1; }
209209

210210
static unsigned getHashValue(const long long& Val) {
211211
return (unsigned)(Val * 37ULL);
@@ -224,12 +224,12 @@ struct DenseMapInfo<std::pair<T, U>> {
224224
using FirstInfo = DenseMapInfo<T>;
225225
using SecondInfo = DenseMapInfo<U>;
226226

227-
static inline Pair getEmptyKey() {
227+
static constexpr Pair getEmptyKey() {
228228
return std::make_pair(FirstInfo::getEmptyKey(),
229229
SecondInfo::getEmptyKey());
230230
}
231231

232-
static inline Pair getTombstoneKey() {
232+
static constexpr Pair getTombstoneKey() {
233233
return std::make_pair(FirstInfo::getTombstoneKey(),
234234
SecondInfo::getTombstoneKey());
235235
}
@@ -257,11 +257,11 @@ struct DenseMapInfo<std::pair<T, U>> {
257257
template <typename... Ts> struct DenseMapInfo<std::tuple<Ts...>> {
258258
using Tuple = std::tuple<Ts...>;
259259

260-
static inline Tuple getEmptyKey() {
260+
static constexpr Tuple getEmptyKey() {
261261
return Tuple(DenseMapInfo<Ts>::getEmptyKey()...);
262262
}
263263

264-
static inline Tuple getTombstoneKey() {
264+
static constexpr Tuple getTombstoneKey() {
265265
return Tuple(DenseMapInfo<Ts>::getTombstoneKey()...);
266266
}
267267

@@ -309,10 +309,22 @@ struct DenseMapInfo<Enum, std::enable_if_t<std::is_enum_v<Enum>>> {
309309
using UnderlyingType = std::underlying_type_t<Enum>;
310310
using Info = DenseMapInfo<UnderlyingType>;
311311

312-
static Enum getEmptyKey() { return static_cast<Enum>(Info::getEmptyKey()); }
312+
// If an enum does not have a "fixed" underlying type, it may be UB to cast
313+
// some values of the underlying type to the enum. We use an "extra" constexpr
314+
// local to ensure that such UB would trigger "static assertion expression is
315+
// not an integral constant expression", rather than runtime UB.
316+
//
317+
// If you hit this error, you can fix by switching to `enum class`, or adding
318+
// an explicit underlying type (e.g. `enum X : int`) to the enum's definition.
313319

314-
static Enum getTombstoneKey() {
315-
return static_cast<Enum>(Info::getTombstoneKey());
320+
static constexpr Enum getEmptyKey() {
321+
constexpr Enum V = static_cast<Enum>(Info::getEmptyKey());
322+
return V;
323+
}
324+
325+
static constexpr Enum getTombstoneKey() {
326+
constexpr Enum V = static_cast<Enum>(Info::getTombstoneKey());
327+
return V;
316328
}
317329

318330
static unsigned getHashValue(const Enum &Val) {
@@ -326,9 +338,9 @@ template <typename T> struct DenseMapInfo<std::optional<T>> {
326338
using Optional = std::optional<T>;
327339
using Info = DenseMapInfo<T>;
328340

329-
static inline Optional getEmptyKey() { return {Info::getEmptyKey()}; }
341+
static constexpr Optional getEmptyKey() { return {Info::getEmptyKey()}; }
330342

331-
static inline Optional getTombstoneKey() { return {Info::getTombstoneKey()}; }
343+
static constexpr Optional getTombstoneKey() { return {Info::getTombstoneKey()}; }
332344

333345
static unsigned getHashValue(const Optional &OptionalVal) {
334346
return detail::combineHashValue(

0 commit comments

Comments
 (0)