Skip to content

Commit 528b7ce

Browse files
committed
[libc] Remove UB specializations of type_traits
The standard specifies that it it UB to specialize the following traits: - std::is_integral - std::is_unsigned - std::make_unsigned - std::make_signed This patch: - Removes specializations for BigInt - Transforms SFINAE for bit.h functions from template parameter to return type (This makes specialization easier). - Adds BigInt specialization for bit.h functions. - Fixes code depending on previous specializations.
1 parent 07f8efa commit 528b7ce

File tree

11 files changed

+275
-112
lines changed

11 files changed

+275
-112
lines changed

libc/src/__support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ add_header_library(
9595
HDRS
9696
integer_to_string.h
9797
DEPENDS
98+
.uint
9899
libc.src.__support.common
99100
libc.src.__support.CPP.algorithm
100101
libc.src.__support.CPP.limits

libc/src/__support/CPP/bit.h

Lines changed: 62 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@ namespace LIBC_NAMESPACE::cpp {
2727

2828
// This implementation of bit_cast requires trivially-constructible To, to avoid
2929
// UB in the implementation.
30-
template <
31-
typename To, typename From,
32-
typename = cpp::enable_if_t<sizeof(To) == sizeof(From) &&
33-
cpp::is_trivially_constructible<To>::value &&
34-
cpp::is_trivially_copyable<To>::value &&
35-
cpp::is_trivially_copyable<From>::value>>
36-
LIBC_INLINE constexpr To bit_cast(const From &from) {
30+
template <typename To, typename From>
31+
LIBC_INLINE constexpr cpp::enable_if_t<
32+
(sizeof(To) == sizeof(From)) &&
33+
cpp::is_trivially_constructible<To>::value &&
34+
cpp::is_trivially_copyable<To>::value &&
35+
cpp::is_trivially_copyable<From>::value,
36+
To>
37+
bit_cast(const From &from) {
3738
MSAN_UNPOISON(&from, sizeof(From));
3839
#if LIBC_HAS_BUILTIN(__builtin_bit_cast)
3940
return __builtin_bit_cast(To, from);
@@ -51,8 +52,10 @@ LIBC_INLINE constexpr To bit_cast(const From &from) {
5152
#endif // LIBC_HAS_BUILTIN(__builtin_bit_cast)
5253
}
5354

54-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
55-
[[nodiscard]] LIBC_INLINE constexpr bool has_single_bit(T value) {
55+
template <typename T>
56+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>,
57+
bool>
58+
has_single_bit(T value) {
5659
return (value != 0) && ((value & (value - 1)) == 0);
5760
}
5861

@@ -70,8 +73,9 @@ template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
7073
/// Only unsigned integral types are allowed.
7174
///
7275
/// Returns cpp::numeric_limits<T>::digits on an input of 0.
73-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
74-
[[nodiscard]] LIBC_INLINE constexpr int countr_zero(T value) {
76+
template <typename T>
77+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
78+
countr_zero(T value) {
7579
if (!value)
7680
return cpp::numeric_limits<T>::digits;
7781
if (value & 0x1)
@@ -103,8 +107,9 @@ ADD_SPECIALIZATION(countr_zero, unsigned long long, __builtin_ctzll)
103107
/// Only unsigned integral types are allowed.
104108
///
105109
/// Returns cpp::numeric_limits<T>::digits on an input of 0.
106-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
107-
[[nodiscard]] LIBC_INLINE constexpr int countl_zero(T value) {
110+
template <typename T>
111+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
112+
countl_zero(T value) {
108113
if (!value)
109114
return cpp::numeric_limits<T>::digits;
110115
// Bisection method.
@@ -135,8 +140,9 @@ ADD_SPECIALIZATION(countl_zero, unsigned long long, __builtin_clzll)
135140
/// Only unsigned integral types are allowed.
136141
///
137142
/// Returns cpp::numeric_limits<T>::digits on an input of all ones.
138-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
139-
[[nodiscard]] LIBC_INLINE constexpr int countl_one(T value) {
143+
template <typename T>
144+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
145+
countl_one(T value) {
140146
return cpp::countl_zero<T>(~value);
141147
}
142148

@@ -147,26 +153,29 @@ template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
147153
/// Only unsigned integral types are allowed.
148154
///
149155
/// Returns cpp::numeric_limits<T>::digits on an input of all ones.
150-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
151-
[[nodiscard]] LIBC_INLINE constexpr int countr_one(T value) {
156+
template <typename T>
157+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
158+
countr_one(T value) {
152159
return cpp::countr_zero<T>(~value);
153160
}
154161

155162
/// Returns the number of bits needed to represent value if value is nonzero.
156163
/// Returns 0 otherwise.
157164
///
158165
/// Ex. bit_width(5) == 3.
159-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
160-
[[nodiscard]] LIBC_INLINE constexpr int bit_width(T value) {
166+
template <typename T>
167+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
168+
bit_width(T value) {
161169
return cpp::numeric_limits<T>::digits - cpp::countl_zero(value);
162170
}
163171

164172
/// Returns the largest integral power of two no greater than value if value is
165173
/// nonzero. Returns 0 otherwise.
166174
///
167175
/// Ex. bit_floor(5) == 4.
168-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
169-
[[nodiscard]] LIBC_INLINE constexpr T bit_floor(T value) {
176+
template <typename T>
177+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
178+
bit_floor(T value) {
170179
if (!value)
171180
return 0;
172181
return T(1) << (cpp::bit_width(value) - 1);
@@ -179,8 +188,9 @@ template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
179188
///
180189
/// The return value is undefined if the input is larger than the largest power
181190
/// of two representable in T.
182-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
183-
[[nodiscard]] LIBC_INLINE constexpr T bit_ceil(T value) {
191+
template <typename T>
192+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
193+
bit_ceil(T value) {
184194
if (value < 2)
185195
return 1;
186196
return T(1) << cpp::bit_width<T>(value - 1u);
@@ -190,28 +200,31 @@ template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
190200
// from https://blog.regehr.org/archives/1063.
191201

192202
// Forward-declare rotr so that rotl can use it.
193-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
194-
[[nodiscard]] LIBC_INLINE constexpr T rotr(T value, int rotate);
203+
template <typename T>
204+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
205+
rotr(T value, int rotate);
195206

196-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
197-
[[nodiscard]] LIBC_INLINE constexpr T rotl(T value, int rotate) {
207+
template <typename T>
208+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
209+
rotl(T value, int rotate) {
198210
constexpr unsigned N = cpp::numeric_limits<T>::digits;
199211
rotate = rotate % N;
200212
if (!rotate)
201213
return value;
202214
if (rotate < 0)
203-
return cpp::rotr(value, -rotate);
215+
return cpp::rotr<T>(value, -rotate);
204216
return (value << rotate) | (value >> (N - rotate));
205217
}
206218

207-
template <typename T, typename>
208-
[[nodiscard]] LIBC_INLINE constexpr T rotr(T value, int rotate) {
219+
template <typename T>
220+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
221+
rotr(T value, int rotate) {
209222
constexpr unsigned N = cpp::numeric_limits<T>::digits;
210223
rotate = rotate % N;
211224
if (!rotate)
212225
return value;
213226
if (rotate < 0)
214-
return cpp::rotl(value, -rotate);
227+
return cpp::rotl<T>(value, -rotate);
215228
return (value >> rotate) | (value << (N - rotate));
216229
}
217230

@@ -226,33 +239,38 @@ LIBC_INLINE constexpr To bit_or_static_cast(const From &from) {
226239
}
227240
}
228241

229-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
230-
[[nodiscard]] LIBC_INLINE constexpr int first_leading_zero(T value) {
242+
template <typename T>
243+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
244+
first_leading_zero(T value) {
231245
return value == cpp::numeric_limits<T>::max() ? 0 : countl_one(value) + 1;
232246
}
233247

234-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
235-
[[nodiscard]] LIBC_INLINE constexpr int first_leading_one(T value) {
248+
template <typename T>
249+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
250+
first_leading_one(T value) {
236251
return first_leading_zero(static_cast<T>(~value));
237252
}
238253

239-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
240-
[[nodiscard]] LIBC_INLINE constexpr int first_trailing_zero(T value) {
254+
template <typename T>
255+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
256+
first_trailing_zero(T value) {
241257
return value == cpp::numeric_limits<T>::max()
242258
? 0
243259
: countr_zero(static_cast<T>(~value)) + 1;
244260
}
245261

246-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
247-
[[nodiscard]] LIBC_INLINE constexpr int first_trailing_one(T value) {
262+
template <typename T>
263+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
264+
first_trailing_one(T value) {
248265
return value == cpp::numeric_limits<T>::max() ? 0 : countr_zero(value) + 1;
249266
}
250267

251268
/// Count number of 1's aka population count or hamming weight.
252269
///
253270
/// Only unsigned integral types are allowed.
254-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
255-
[[nodiscard]] LIBC_INLINE constexpr int count_ones(T value) {
271+
template <typename T>
272+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
273+
count_ones(T value) {
256274
int count = 0;
257275
for (int i = 0; i != cpp::numeric_limits<T>::digits; ++i)
258276
if ((value >> i) & 0x1)
@@ -272,8 +290,9 @@ ADD_SPECIALIZATION(unsigned long long, __builtin_popcountll)
272290
// TODO: 128b specializations?
273291
#undef ADD_SPECIALIZATION
274292

275-
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
276-
[[nodiscard]] LIBC_INLINE constexpr int count_zeros(T value) {
293+
template <typename T>
294+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
295+
count_zeros(T value) {
277296
return count_ones<T>(static_cast<T>(~value));
278297
}
279298

0 commit comments

Comments
 (0)