Skip to content

Commit

Permalink
[libc++][test] Fixes constexpr nasty_char_traits.
Browse files Browse the repository at this point in the history
This was discovered by @StephanTLavavej who provided the solution they use
in MSVC STL. This solution is based on that example.

The same issue affects the constexpr_char_traits which was discovered in
llvm#88389. This uses the same fix.

Fixes: llvm#74221
  • Loading branch information
mordante committed May 3, 2024
1 parent a06c1fe commit b791541
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 21 deletions.
56 changes: 39 additions & 17 deletions libcxx/test/support/constexpr_char_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,30 @@

#include "test_macros.h"

// Tests whether the range [p1, p1 + n) overlaps with the range [p2, p2 + n).
//
// precondition The ranges [p1, p1 + n) and [p2, p2 + n) are valid ranges.
//
// Typically the pointers are compared with less than. This is not allowed when
// the pointers belong to different ranges. This is UB. Typically, this is
// benign at run-time, however since UB is not allowed during constant
// evaluation this does not compile. This function does the validation without
// UB.
//
// When the ranges overlap the ranges can be copied from the beginning to the
// end. Otherwise they need to be copied from the end to the beginning.
template <class Traits>
TEST_CONSTEXPR_CXX14 bool is_overlapping_range(Traits* p1, const Traits* p2, std::size_t n) {
if (p1 == p2) // Needed when n == 0
return true;

for (; n; --n, ++p1)
if (p1 == p2)
return true;

return false;
}

template <class CharT>
struct constexpr_char_traits
{
Expand Down Expand Up @@ -98,23 +122,21 @@ constexpr_char_traits<CharT>::find(const char_type* s, std::size_t n, const char
}

template <class CharT>
TEST_CONSTEXPR_CXX14 CharT*
constexpr_char_traits<CharT>::move(char_type* s1, const char_type* s2, std::size_t n)
{
char_type* r = s1;
if (s1 < s2)
{
for (; n; --n, ++s1, ++s2)
assign(*s1, *s2);
}
else if (s2 < s1)
{
s1 += n;
s2 += n;
for (; n; --n)
assign(*--s1, *--s2);
}
return r;
TEST_CONSTEXPR_CXX14 CharT* constexpr_char_traits<CharT>::move(char_type* s1, const char_type* s2, std::size_t n) {
if (s1 == s2)
return s1;

char_type* r = s1;
if (is_overlapping_range(s1, s2, n)) {
for (; n; --n)
assign(*s1++, *s2++);
} else {
s1 += n;
s2 += n;
for (; n; --n)
assign(*--s1, *--s2);
}
return r;
}

template <class CharT>
Expand Down
12 changes: 8 additions & 4 deletions libcxx/test/support/nasty_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "make_string.h"
#include "test_macros.h"
#include "constexpr_char_traits.h" // is_nonoverlapping_range

// This defines a nasty_string similar to nasty_containers. This string's
// value_type does operator hijacking, which allows us to ensure that the
Expand Down Expand Up @@ -118,11 +119,14 @@ constexpr const nasty_char* nasty_char_traits::find(const nasty_char* s, std::si
}

constexpr nasty_char* nasty_char_traits::move(nasty_char* s1, const nasty_char* s2, std::size_t n) {
if (s1 == s2)
return s1;

nasty_char* r = s1;
if (s1 < s2) {
for (; n; --n, ++s1, ++s2)
assign(*s1, *s2);
} else if (s2 < s1) {
if (is_overlapping_range(s1, s2, n)) {
for (; n; --n)
assign(*s1++, *s2++);
} else {
s1 += n;
s2 += n;
for (; n; --n)
Expand Down

0 comments on commit b791541

Please sign in to comment.