Skip to content

Commit

Permalink
[libc++] Fix rejects-valid in std::span copy construction (llvm#104500)
Browse files Browse the repository at this point in the history
Trying to copy-construct a std::span from another std::span holding an
incomplete type would fail as we evaluate the SFINAE for the range-based
constructor. The problem was that we checked for __is_std_span after
checking for the range being a contiguous_range, which hard-errored
because of arithmetic on a pointer to incomplete type.

As a drive-by, refactor the whole test and format it.

Fixes llvm#104496

(cherry picked from commit 99696b3)
  • Loading branch information
ldionne authored and llvmbot committed Aug 16, 2024
1 parent 4d4a410 commit 64b1a3e
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 42 deletions.
2 changes: 1 addition & 1 deletion libcxx/include/span
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,10 @@ struct __is_std_span<span<_Tp, _Sz>> : true_type {};

template <class _Range, class _ElementType>
concept __span_compatible_range =
!__is_std_span<remove_cvref_t<_Range>>::value && //
ranges::contiguous_range<_Range> && //
ranges::sized_range<_Range> && //
(ranges::borrowed_range<_Range> || is_const_v<_ElementType>) && //
!__is_std_span<remove_cvref_t<_Range>>::value && //
!__is_std_array<remove_cvref_t<_Range>>::value && //
!is_array_v<remove_cvref_t<_Range>> && //
is_convertible_v<remove_reference_t<ranges::range_reference_t<_Range>> (*)[], _ElementType (*)[]>;
Expand Down
126 changes: 85 additions & 41 deletions libcxx/test/std/containers/views/views.span/span.cons/copy.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17

// <span>
Expand All @@ -14,58 +15,101 @@
#include <span>
#include <cassert>
#include <string>
#include <utility>

#include "test_macros.h"

template <typename T>
constexpr bool doCopy(const T &rhs)
{
ASSERT_NOEXCEPT(T{rhs});
T lhs{rhs};
return lhs.data() == rhs.data()
&& lhs.size() == rhs.size();
}
template <class T>
constexpr void test() {
ASSERT_NOEXCEPT(std::span<T>(std::declval<std::span<T> const&>()));
ASSERT_NOEXCEPT(std::span<T>{std::declval<std::span<T> const&>()});

struct A{};

template <typename T>
void testCV ()
{
int arr[] = {1,2,3};
assert((doCopy(std::span<T> () )));
assert((doCopy(std::span<T,0>() )));
assert((doCopy(std::span<T> (&arr[0], 1))));
assert((doCopy(std::span<T,1>(&arr[0], 1))));
assert((doCopy(std::span<T> (&arr[0], 2))));
assert((doCopy(std::span<T,2>(&arr[0], 2))));
// dynamic_extent
{
std::span<T> x;
std::span<T> copy(x);
assert(copy.data() == x.data());
assert(copy.size() == x.size());
}
{
T array[3] = {};
std::span<T> x(array, 3);
std::span<T> copy(x);
assert(copy.data() == array);
assert(copy.size() == 3);
}
{
T array[3] = {};
std::span<T> x(array, 2);
std::span<T> copy(x);
assert(copy.data() == array);
assert(copy.size() == 2);
}

// static extent
{
std::span<T, 0> x;
std::span<T, 0> copy(x);
assert(copy.data() == x.data());
assert(copy.size() == x.size());
}
{
T array[3] = {};
std::span<T, 3> x(array);
std::span<T, 3> copy(x);
assert(copy.data() == array);
assert(copy.size() == 3);
}
{
T array[2] = {};
std::span<T, 2> x(array);
std::span<T, 2> copy(x);
assert(copy.data() == array);
assert(copy.size() == 2);
}
}

struct Foo {};

constexpr bool test_all() {
test<int>();
test<const int>();
test<volatile int>();
test<const volatile int>();

int main(int, char**)
{
constexpr int carr[] = {1,2,3};
test<long>();
test<const long>();
test<volatile long>();
test<const volatile long>();

static_assert(doCopy(std::span< int> ()), "");
static_assert(doCopy(std::span< int,0>()), "");
static_assert(doCopy(std::span<const int> (&carr[0], 1)), "");
static_assert(doCopy(std::span<const int,1>(&carr[0], 1)), "");
static_assert(doCopy(std::span<const int> (&carr[0], 2)), "");
static_assert(doCopy(std::span<const int,2>(&carr[0], 2)), "");
test<double>();
test<const double>();
test<volatile double>();
test<const volatile double>();

static_assert(doCopy(std::span<long>()), "");
static_assert(doCopy(std::span<double>()), "");
static_assert(doCopy(std::span<A>()), "");
// Note: Can't test non-fundamental types with volatile because we require `T*` to be indirectly_readable,
// which isn't the case when T is volatile.
test<Foo>();
test<const Foo>();

std::string s;
assert(doCopy(std::span<std::string> () ));
assert(doCopy(std::span<std::string, 0>() ));
assert(doCopy(std::span<std::string> (&s, 1)));
assert(doCopy(std::span<std::string, 1>(&s, 1)));
test<std::string>();
test<const std::string>();

// Regression test for https://github.com/llvm/llvm-project/issues/104496
{
struct Incomplete;
std::span<Incomplete> x;
std::span<Incomplete> copy(x);
assert(copy.data() == x.data());
assert(copy.size() == x.size());
}

return true;
}

testCV< int>();
testCV<const int>();
testCV< volatile int>();
testCV<const volatile int>();
int main(int, char**) {
test_all();
static_assert(test_all());

return 0;
}

0 comments on commit 64b1a3e

Please sign in to comment.