-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[libc++] Fix bitset conversion functions and refactor constructor #121348
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
4d20d1e
to
5751c13
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah. I think the change is actually "Fix possible out of range access in bitset::to_ullong
implementation". The current title and PR description are indescriptive to me.
The intented change look good to me. But I guess we need far more changes to properly support 16-bit platforms. Has this been discussed before?
94e34bb
to
f85d6e7
Compare
✅ With the latest revision this PR passed the C/C++ code formatter. |
67e021d
to
c86557e
Compare
This patch is not just addressing issue for This patch fixes the previous implementation for different scenarios with |
@winner245 You're still adding branches here which are effectively dead code AFAICT. I'm not against fixing bugs, but I am against introducing effectively dead code. Given that your commit message starts with that case, it seems like it is the most important part of the patch. If it's not, I'd rip these parts out to make the actual intention clear. |
I think I'm not following fully here. Looking at the code after the patch, I don't see what part of it is dead code. We do have platforms where If I missed something and there is indeed dead code being introduced, then I agree that we shouldn't be checking in dead code -- but I'm not seeing anything dead in the latest version of the patch. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I missed something and there is indeed dead code being introduced, then I agree that we shouldn't be checking in dead code -- but I'm not seeing anything dead in the latest version of the patch.
I've left a comment where I think we're introducing dead code.
Edit:
Perhaps the commit message needs to be adjusted to explain the motivation more clearly?
I think so. Starting with something that happens to work now and explain extensively why isn't exactly screaming "falls out of this patch" but rather "this is the main goal of this patch".
libcxx/include/bitset
Outdated
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const { return 0UL; } | ||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const { return 0ULL; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reverted.
// UNSUPPORTED: no-exceptions | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should only guard the cases where exceptions are required. Otherwise we lose all coverage for to_ullong
with exceptions disabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I agree we should only guard the test cases which actually check exceptions. I am now using #ifndef TEST_HAS_NO_EXCEPTIONS
around the test cases.
libcxx/include/bitset
Outdated
|
||
// unsigned long may span multiple words which are concatenated to form the result | ||
template <typename _StorageType = __storage_type, | ||
__enable_if_t<sizeof(_StorageType) < sizeof(unsigned long), int> = 0> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is never true AFAICT. On all 32 bit platforms sizeof(unsigned long)
is at most 32 bits, and sizeof(_StorageType)
(i.e. sizeof(size_t)
) should always be 32 bits as well.
On 64 bit platforms sizeof(size_t)
is always eight, and I'm quite certain there is no platform where unsigned long
is larger than that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As suggested, I've removed this code branch, and I am now focusing only on the case where sizeof(size_t) >= sizeof(unsigned long)
.
libcxx/include/bitset
Outdated
@@ -80,7 +80,7 @@ public: | |||
constexpr bool operator[](size_t pos) const; | |||
reference operator[](size_t pos); // constexpr since C++23 | |||
unsigned long to_ulong() const; // constexpr since C++23 | |||
unsigned long long to_ullong() const; // constexpr since C++23 | |||
unsigned long long to_ullong() const; // since C++11, constexpr since C++23 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems incorrect?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is LWG694. I don't think LWG694 retroactively applied to C++03 because long long
was added in C++11 as a new feature. But since libc++ supports many C++11 features in C++03 mode, perhaps it's also fine not to mention "since C++11".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we don't mention "sine C++11" in most places, since we back-port a humongous amount of code from C++11.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this function was in C++11 standard mode, but we backported it to C++03. So I've now removed the "since C++11" description.
libcxx/include/bitset
Outdated
// TODO: This is a workaround for a gdb test failure (gdb_pretty_printer_test.sh.cpp) in | ||
// stage1 CI (generic-gcc, gcc-14, g++-14), due to the __bits_per_word name lookup failure | ||
// if not referenced in the constructor initializer list. | ||
// See: https://github.com/llvm/llvm-project/actions/runs/15071518915/job/42368867929?pr=121348#logs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// TODO: This is a workaround for a gdb test failure (gdb_pretty_printer_test.sh.cpp) in | |
// stage1 CI (generic-gcc, gcc-14, g++-14), due to the __bits_per_word name lookup failure | |
// if not referenced in the constructor initializer list. | |
// See: https://github.com/llvm/llvm-project/actions/runs/15071518915/job/42368867929?pr=121348#logs | |
// TODO: We must refer to __bits_per_word in order to work around an issue with the GDB pretty-printers. | |
// Without it, the pretty-printers complain about a missing __bits_per_word member. This must | |
// be investigated. |
cf28025
to
958a1c3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This resolves my concerns, thanks! I'll leave the rest of the review to Louis.
libcxx/include/bitset
Outdated
"bitset only supports platforms where sizeof(size_t) >= sizeof(unsigned long), such as 32-bit and " | ||
"64-bit platforms"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"bitset only supports platforms where sizeof(size_t) >= sizeof(unsigned long), such as 32-bit and " | |
"64-bit platforms"); | |
"libc++ only supports platforms where sizeof(size_t) >= sizeof(unsigned long), such as 32-bit and " | |
"64-bit platforms. If you're interested in supporting a platform where that is not the case, please contact the libc++ developers."); |
Otherwise people might think that we have no interest in supporting their platform.
I've completely rewritten the commit message, which now has a much clearer motivation. |
958a1c3
to
746e44b
Compare
746e44b
to
db4565b
Compare
libcxx/test/std/utilities/template.bitset/bitset.members/to_ullong.pass.cpp
Outdated
Show resolved
Hide resolved
libcxx/test/std/utilities/template.bitset/bitset.members/to_ulong.pass.cpp
Outdated
Show resolved
Hide resolved
3f7d57b
to
3700be1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
…vm#121348) This patch addresses several implementation issues in `bitset`'s conversion functions `to_ullong` and `to_ulong`, and refactors its converting constructor `__bitset(unsigned long long __v)` to a more generic and elegant implementation.
…vm#121348) This patch addresses several implementation issues in `bitset`'s conversion functions `to_ullong` and `to_ulong`, and refactors its converting constructor `__bitset(unsigned long long __v)` to a more generic and elegant implementation.
This patch addresses several implementation issues in
bitset
's conversion functionsto_ullong
andto_ulong
, and refactors its converting constructor__bitset(unsigned long long __v)
to a more generic and elegant implementation.to_ullong:
llvm-project/libcxx/include/bitset
Lines 384 to 385 in b7637a8
The existing implementation incorrectly concatenates multiple words using a fixed shifting amount
__bits_per_word
inside a loop. The correct shifting amount should be__i * __bits_per_word
, which depends on the loop index__i
. This is now correctly implemented in this patch.Additionally, the previous implementation relied on tag-dispatching, which became difficult to follow due to the combination of multiple tags. This patch improves readability by replacing tag-dispatching with
constexpr if
, placing conditions in-line for a more straightforward and maintainable approach.to_ulong:
llvm-project/libcxx/include/bitset
Lines 527 to 530 in b7637a8
The
to_ulong(unsigned long )
function in the one-word specialization template__bitset<1, _Size>
is not standard-conforming as it unconditionally returns__first_
, while the standard requires that it throwsstd::overflow_error
if the value cannot fit withinunsigned long
(i.e.,sizeof(size_t) > sizeof(unsigned long)
). On LLP64 (e.g., Windows and MinGW), we havesizeof(size_t) = 64
andsizeof(unsigned long) = 32
(according to: https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models), and in this case, we should throwstd::overflow_error
. This is now fixed in this PR.__bitset(unsigned long long __v):
The converting constructor is now refactored using a variadic template and an internal implementation of
integer_sequence
(to support C++03). This approach eliminates conditional preprocessing statements, improving readability and maintainability.