Skip to content

Commit 64b1a3e

Browse files
ldionnellvmbot
authored andcommitted
[libc++] Fix rejects-valid in std::span copy construction (llvm#104500)
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)
1 parent 4d4a410 commit 64b1a3e

File tree

2 files changed

+86
-42
lines changed

2 files changed

+86
-42
lines changed

libcxx/include/span

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,10 @@ struct __is_std_span<span<_Tp, _Sz>> : true_type {};
206206

207207
template <class _Range, class _ElementType>
208208
concept __span_compatible_range =
209+
!__is_std_span<remove_cvref_t<_Range>>::value && //
209210
ranges::contiguous_range<_Range> && //
210211
ranges::sized_range<_Range> && //
211212
(ranges::borrowed_range<_Range> || is_const_v<_ElementType>) && //
212-
!__is_std_span<remove_cvref_t<_Range>>::value && //
213213
!__is_std_array<remove_cvref_t<_Range>>::value && //
214214
!is_array_v<remove_cvref_t<_Range>> && //
215215
is_convertible_v<remove_reference_t<ranges::range_reference_t<_Range>> (*)[], _ElementType (*)[]>;

libcxx/test/std/containers/views/views.span/span.cons/copy.pass.cpp

+85-41
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
77
//===----------------------------------------------------------------------===//
8+
89
// UNSUPPORTED: c++03, c++11, c++14, c++17
910

1011
// <span>
@@ -14,58 +15,101 @@
1415
#include <span>
1516
#include <cassert>
1617
#include <string>
18+
#include <utility>
1719

1820
#include "test_macros.h"
1921

20-
template <typename T>
21-
constexpr bool doCopy(const T &rhs)
22-
{
23-
ASSERT_NOEXCEPT(T{rhs});
24-
T lhs{rhs};
25-
return lhs.data() == rhs.data()
26-
&& lhs.size() == rhs.size();
27-
}
22+
template <class T>
23+
constexpr void test() {
24+
ASSERT_NOEXCEPT(std::span<T>(std::declval<std::span<T> const&>()));
25+
ASSERT_NOEXCEPT(std::span<T>{std::declval<std::span<T> const&>()});
2826

29-
struct A{};
30-
31-
template <typename T>
32-
void testCV ()
33-
{
34-
int arr[] = {1,2,3};
35-
assert((doCopy(std::span<T> () )));
36-
assert((doCopy(std::span<T,0>() )));
37-
assert((doCopy(std::span<T> (&arr[0], 1))));
38-
assert((doCopy(std::span<T,1>(&arr[0], 1))));
39-
assert((doCopy(std::span<T> (&arr[0], 2))));
40-
assert((doCopy(std::span<T,2>(&arr[0], 2))));
27+
// dynamic_extent
28+
{
29+
std::span<T> x;
30+
std::span<T> copy(x);
31+
assert(copy.data() == x.data());
32+
assert(copy.size() == x.size());
33+
}
34+
{
35+
T array[3] = {};
36+
std::span<T> x(array, 3);
37+
std::span<T> copy(x);
38+
assert(copy.data() == array);
39+
assert(copy.size() == 3);
40+
}
41+
{
42+
T array[3] = {};
43+
std::span<T> x(array, 2);
44+
std::span<T> copy(x);
45+
assert(copy.data() == array);
46+
assert(copy.size() == 2);
47+
}
48+
49+
// static extent
50+
{
51+
std::span<T, 0> x;
52+
std::span<T, 0> copy(x);
53+
assert(copy.data() == x.data());
54+
assert(copy.size() == x.size());
55+
}
56+
{
57+
T array[3] = {};
58+
std::span<T, 3> x(array);
59+
std::span<T, 3> copy(x);
60+
assert(copy.data() == array);
61+
assert(copy.size() == 3);
62+
}
63+
{
64+
T array[2] = {};
65+
std::span<T, 2> x(array);
66+
std::span<T, 2> copy(x);
67+
assert(copy.data() == array);
68+
assert(copy.size() == 2);
69+
}
4170
}
4271

72+
struct Foo {};
73+
74+
constexpr bool test_all() {
75+
test<int>();
76+
test<const int>();
77+
test<volatile int>();
78+
test<const volatile int>();
4379

44-
int main(int, char**)
45-
{
46-
constexpr int carr[] = {1,2,3};
80+
test<long>();
81+
test<const long>();
82+
test<volatile long>();
83+
test<const volatile long>();
4784

48-
static_assert(doCopy(std::span< int> ()), "");
49-
static_assert(doCopy(std::span< int,0>()), "");
50-
static_assert(doCopy(std::span<const int> (&carr[0], 1)), "");
51-
static_assert(doCopy(std::span<const int,1>(&carr[0], 1)), "");
52-
static_assert(doCopy(std::span<const int> (&carr[0], 2)), "");
53-
static_assert(doCopy(std::span<const int,2>(&carr[0], 2)), "");
85+
test<double>();
86+
test<const double>();
87+
test<volatile double>();
88+
test<const volatile double>();
5489

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

59-
std::string s;
60-
assert(doCopy(std::span<std::string> () ));
61-
assert(doCopy(std::span<std::string, 0>() ));
62-
assert(doCopy(std::span<std::string> (&s, 1)));
63-
assert(doCopy(std::span<std::string, 1>(&s, 1)));
95+
test<std::string>();
96+
test<const std::string>();
97+
98+
// Regression test for https://github.com/llvm/llvm-project/issues/104496
99+
{
100+
struct Incomplete;
101+
std::span<Incomplete> x;
102+
std::span<Incomplete> copy(x);
103+
assert(copy.data() == x.data());
104+
assert(copy.size() == x.size());
105+
}
106+
107+
return true;
108+
}
64109

65-
testCV< int>();
66-
testCV<const int>();
67-
testCV< volatile int>();
68-
testCV<const volatile int>();
110+
int main(int, char**) {
111+
test_all();
112+
static_assert(test_all());
69113

70114
return 0;
71115
}

0 commit comments

Comments
 (0)