Skip to content

Commit dbbeee6

Browse files
H-G-HristovZingam
andauthored
[libc++][span] P2447R4: std::span over an initializer list (#78157)
Implements: https://wg21.link/P2447R6 - https://eel.is/c++draft/span.syn - https://eel.is/c++draft/span.overview - https://eel.is/c++draft/span.cons - https://eel.is/c++draft/diff --------- Co-authored-by: Zingam <[email protected]>
1 parent 552f554 commit dbbeee6

File tree

13 files changed

+238
-46
lines changed

13 files changed

+238
-46
lines changed

libcxx/docs/FeatureTestMacroTable.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ Status
438438
--------------------------------------------------- -----------------
439439
``__cpp_lib_span_at`` ``202311L``
440440
--------------------------------------------------- -----------------
441-
``__cpp_lib_span_initializer_list`` *unimplemented*
441+
``__cpp_lib_span_initializer_list`` ``202311L``
442442
--------------------------------------------------- -----------------
443443
``__cpp_lib_sstream_from_string_view`` *unimplemented*
444444
--------------------------------------------------- -----------------

libcxx/docs/ReleaseNotes/18.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Implemented Papers
6161
- P1759R6 - Native handles and file streams
6262
- P2868R3 - Remove Deprecated ``std::allocator`` Typedef From C++26
6363
- P2517R1 - Add a conditional ``noexcept`` specification to ``std::apply``
64+
- P2447R6 - ``span`` over initializer list
6465

6566

6667
Improvements and New Features

libcxx/docs/Status/Cxx2cPapers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"`P2918R2 <https://wg21.link/P2918R2>`__","LWG","Runtime format strings II","Kona November 2023","|Complete|","18.0","|format|"
3535
"`P2909R4 <https://wg21.link/P2909R4>`__","LWG","Fix formatting of code units as integers (Dude, where’s my ``char``?)","Kona November 2023","|Complete|","18.0","|format| |DR|"
3636
"`P0952R2 <https://wg21.link/P0952R2>`__","LWG","A new specification for ``std::generate_canonical``","Kona November 2023","","",""
37-
"`P2447R6 <https://wg21.link/P2447R6>`__","LWG","``std::span`` over an initializer list","Kona November 2023","","",""
37+
"`P2447R6 <https://wg21.link/P2447R6>`__","LWG","``std::span`` over an initializer list","Kona November 2023","|Complete|","18.0",""
3838
"`P2821R5 <https://wg21.link/P2821R5>`__","LWG","``span.at()``","Kona November 2023","|Complete|","18.0",""
3939
"`P2868R3 <https://wg21.link/P2868R3>`__","LWG","Remove Deprecated ``std::allocator`` Typedef From C++26","Kona November 2023","|Complete|","18.0",""
4040
"`P2870R3 <https://wg21.link/P2870R3>`__","LWG","Remove ``basic_string::reserve()`` From C++26","Kona November 2023","|Complete|","18.0",""

libcxx/include/span

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public:
6868
constexpr span(const array<value_type, N>& arr) noexcept;
6969
template<class R>
7070
constexpr explicit(Extent != dynamic_extent) span(R&& r);
71+
constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
7172
constexpr span(const span& other) noexcept = default;
7273
template <class OtherElementType, size_t OtherExtent>
7374
constexpr explicit(Extent != dynamic_extent) span(const span<OtherElementType, OtherExtent>& s) noexcept;
@@ -228,6 +229,15 @@ public:
228229
requires(_Sz == 0)
229230
_LIBCPP_HIDE_FROM_ABI constexpr span() noexcept : __data_{nullptr} {}
230231

232+
# if _LIBCPP_STD_VER >= 26
233+
_LIBCPP_HIDE_FROM_ABI constexpr explicit span(std::initializer_list<value_type> __il)
234+
requires is_const_v<element_type>
235+
: __data_{__il.begin()} {
236+
_LIBCPP_ASSERT_VALID_INPUT_RANGE(
237+
_Extent == __il.size(), "Size mismatch in span's constructor _Extent != __il.size().");
238+
}
239+
# endif
240+
231241
constexpr span(const span&) noexcept = default;
232242
constexpr span& operator=(const span&) noexcept = default;
233243

@@ -397,6 +407,12 @@ public:
397407
// [span.cons], span constructors, copy, assignment, and destructor
398408
_LIBCPP_HIDE_FROM_ABI constexpr span() noexcept : __data_{nullptr}, __size_{0} {}
399409

410+
# if _LIBCPP_STD_VER >= 26
411+
_LIBCPP_HIDE_FROM_ABI constexpr span(std::initializer_list<value_type> __il)
412+
requires is_const_v<element_type>
413+
: __data_{__il.begin()}, __size_{__il.size()} {}
414+
# endif
415+
400416
constexpr span(const span&) noexcept = default;
401417
constexpr span& operator=(const span&) noexcept = default;
402418

libcxx/include/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
507507
// # define __cpp_lib_saturation_arithmetic 202311L
508508
// # define __cpp_lib_smart_ptr_owner_equality 202306L
509509
# define __cpp_lib_span_at 202311L
510-
// # define __cpp_lib_span_initializer_list 202311L
510+
# define __cpp_lib_span_initializer_list 202311L
511511
// # define __cpp_lib_sstream_from_string_view 202306L
512512
// # define __cpp_lib_submdspan 202306L
513513
// # define __cpp_lib_text_encoding 202306L

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,11 @@ constexpr bool testSpan()
9494
assert(s4.data() == val && s4.size() == 2);
9595

9696
std::span<const int> s5 = {{1,2}};
97+
#if TEST_STD_VER >= 26
98+
std::span<const int, 2> s6({1, 2});
99+
#else
97100
std::span<const int, 2> s6 = {{1,2}};
101+
#endif
98102
assert(s5.size() == 2); // and it dangles
99103
assert(s6.size() == 2); // and it dangles
100104

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
10+
11+
// REQUIRES: has-unix-headers
12+
// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
13+
// XFAIL: availability-verbose_abort-missing
14+
15+
// <span>
16+
17+
// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
18+
19+
#include <cassert>
20+
#include <initializer_list>
21+
#include <span>
22+
23+
#include "check_assertion.h"
24+
25+
int main(int, char**) {
26+
TEST_LIBCPP_ASSERT_FAILURE(
27+
(std::span<const int, 4>({1, 2, 3, 9084, 5})), "Size mismatch in span's constructor _Extent != __il.size().");
28+
TEST_LIBCPP_ASSERT_FAILURE((std::span<const int, 4>(std::initializer_list<int>{1, 2, 3, 9084, 5})),
29+
"Size mismatch in span's constructor _Extent != __il.size().");
30+
31+
return 0;
32+
}

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

Lines changed: 110 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,134 @@
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>
1112

12-
#include <span>
13+
// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
14+
15+
#include <any>
1316
#include <cassert>
1417
#include <cstddef>
18+
#include <initializer_list>
19+
#include <span>
20+
#include <type_traits>
21+
22+
#include "test_convertible.h"
23+
#include "test_macros.h"
24+
25+
#if TEST_STD_VER >= 26
26+
27+
// SFINAE
28+
29+
template <typename T>
30+
concept ConstElementType = std::is_const_v<typename T::element_type>;
31+
32+
static_assert(ConstElementType<std::span<const int>>);
33+
static_assert(!ConstElementType<std::span<int>>);
34+
static_assert(ConstElementType<std::span<const int, 94>>);
35+
static_assert(!ConstElementType<std::span<int, 94>>);
36+
37+
// Constructor constraings
38+
39+
template <typename I, typename T, std::size_t... N>
40+
concept HasInitializerListCtr = requires(I il) { std::span<T, N...>{il}; };
41+
42+
static_assert(HasInitializerListCtr<std::initializer_list<const int>, const int>);
43+
static_assert(!HasInitializerListCtr<std::initializer_list<int>, int>);
44+
static_assert(HasInitializerListCtr<std::initializer_list<const int>, const int, 94>);
45+
static_assert(!HasInitializerListCtr<std::initializer_list<int>, int, 94>);
46+
47+
// Constructor conditionally explicit
48+
49+
static_assert(!test_convertible<std::span<const int, 28>, std::initializer_list<int>>(),
50+
"This constructor must be explicit");
51+
static_assert(std::is_constructible_v<std::span<const int, 28>, std::initializer_list<int>>);
52+
static_assert(test_convertible<std::span<const int>, std::initializer_list<int>>(),
53+
"This constructor must not be explicit");
54+
static_assert(std::is_constructible_v<std::span<const int>, std::initializer_list<int>>);
55+
56+
#endif
1557

1658
struct Sink {
17-
constexpr Sink() = default;
18-
constexpr Sink(Sink*) {}
59+
constexpr Sink() = default;
60+
constexpr Sink(Sink*) {}
1961
};
2062

21-
constexpr std::size_t count(std::span<const Sink> sp) {
22-
return sp.size();
23-
}
63+
constexpr std::size_t count(std::span<const Sink> sp) { return sp.size(); }
2464

25-
template<int N>
26-
constexpr std::size_t countn(std::span<const Sink, N> sp) {
27-
return sp.size();
65+
template <std::size_t N>
66+
constexpr std::size_t count_n(std::span<const Sink, N> sp) {
67+
return sp.size();
2868
}
2969

3070
constexpr bool test() {
71+
#if TEST_STD_VER >= 26
72+
// Dynamic extent
73+
{
74+
Sink a[10];
75+
76+
assert(count({a}) == 1);
77+
assert(count({a, a + 10}) == 2);
78+
assert(count({a, a + 1, a + 2}) == 3);
79+
assert(count(std::initializer_list<Sink>{a[0], a[1], a[2], a[3]}) == 4);
80+
}
81+
#else
82+
{
3183
Sink a[10];
84+
3285
assert(count({a}) == 10);
33-
assert(count({a, a+10}) == 10);
34-
assert(countn<10>({a}) == 10);
35-
return true;
86+
assert(count({a, a + 10}) == 10);
87+
assert(count_n<10>({a}) == 10);
88+
}
89+
#endif
90+
91+
return true;
92+
}
93+
94+
// Test P2447R4 "Annex C examples"
95+
96+
constexpr int three(std::span<void* const> sp) { return sp.size(); }
97+
98+
constexpr int four(std::span<const std::any> sp) { return sp.size(); }
99+
100+
bool test_P2447R4_annex_c_examples() {
101+
// 1. Overload resolution is affected
102+
// --> tested in "initializer_list.verify.cpp"
103+
104+
// 2. The `initializer_list` ctor has high precedence
105+
// --> tested in "initializer_list.verify.cpp"
106+
107+
// 3. Implicit two-argument construction with a highly convertible value_type
108+
#if TEST_STD_VER >= 26
109+
{
110+
void* a[10];
111+
assert(three({a, 0}) == 2);
112+
}
113+
{
114+
std::any a[10];
115+
assert(four({a, a + 10}) == 2);
116+
}
117+
#else
118+
{
119+
void* a[10];
120+
assert(three({a, 0}) == 0);
121+
}
122+
{
123+
std::any a[10];
124+
assert(four({a, a + 10}) == 10);
125+
}
126+
#endif
127+
128+
return true;
36129
}
37130

38131
int main(int, char**) {
39-
test();
40-
static_assert(test());
132+
assert(test());
133+
static_assert(test());
134+
135+
assert(test_P2447R4_annex_c_examples());
41136

42-
return 0;
137+
return 0;
43138
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17
10+
11+
// <span>
12+
13+
// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
14+
15+
#include <span>
16+
#include <utility>
17+
18+
#include "test_macros.h"
19+
20+
// Test P2447R4 "Annex C examples"
21+
22+
void one(std::pair<int, int>);
23+
void one(std::span<const int>);
24+
25+
void two(std::span<const int, 2>);
26+
27+
void test_P2447R4_annex_c_examples() {
28+
// 1. Overload resolution is affected
29+
#if TEST_STD_VER >= 26
30+
// expected-error@+1 {{call to 'one' is ambiguous}}
31+
one({1, 2});
32+
#else
33+
// expected-no-diagnostics
34+
one({1, 2});
35+
#endif
36+
37+
// 2. The `initializer_list` ctor has high precedence
38+
#if TEST_STD_VER >= 26
39+
// expected-error@+1 {{chosen constructor is explicit in copy-initialization}}
40+
two({{1, 2}});
41+
#else
42+
// expected-no-diagnostics
43+
two({{1, 2}});
44+
#endif
45+
46+
// 3. Implicit two-argument construction with a highly convertible value_type
47+
// --> tested in "initializer_list.pass.cpp"
48+
}

libcxx/test/std/containers/views/views.span/span.cons/iterator_len.verify.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,31 @@
1313
// constexpr explicit(Extent != dynamic_extent) span(It first, size_type count);
1414
// If Extent is not equal to dynamic_extent, then count shall be equal to Extent.
1515
//
16+
// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
1617

1718
#include <span>
1819
#include <cstddef>
1920

21+
#include "test_macros.h"
22+
2023
template <class T, std::size_t extent>
2124
std::span<T, extent> createImplicitSpan(T* ptr, std::size_t len) {
2225
return {ptr, len}; // expected-error {{chosen constructor is explicit in copy-initialization}}
2326
}
2427

25-
void f() {
28+
void test() {
2629
// explicit constructor necessary
2730
int arr[] = {1, 2, 3};
2831
createImplicitSpan<int, 1>(arr, 3);
2932

30-
std::span<int> sp = {0, 0}; // expected-error {{no matching constructor for initialization of 'std::span<int>'}}
31-
std::span<int, 2> sp2 = {0, 0}; // expected-error {{no matching constructor for initialization of 'std::span<int, 2>'}}
32-
std::span<const int> csp = {0, 0}; // expected-error {{no matching constructor for initialization of 'std::span<const int>'}}
33-
std::span<const int, 2> csp2 = {0, 0}; // expected-error {{no matching constructor for initialization of 'std::span<const int, 2>'}}
33+
// expected-error@+1 {{no matching constructor for initialization of 'std::span<int>'}}
34+
std::span<int> sp = {0, 0};
35+
// expected-error@+1 {{no matching constructor for initialization of 'std::span<int, 2>'}}
36+
std::span<int, 2> sp2 = {0, 0};
37+
#if TEST_STD_VER < 26
38+
// expected-error@+1 {{no matching constructor for initialization of 'std::span<const int>'}}
39+
std::span<const int> csp = {0, 0};
40+
// expected-error@+1 {{no matching constructor for initialization of 'std::span<const int, 2>'}}
41+
std::span<const int, 2> csp2 = {0, 0};
42+
#endif
3443
}

libcxx/test/std/language.support/support.limits/support.limits.general/span.version.compile.pass.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -116,17 +116,11 @@
116116
# error "__cpp_lib_span_at should have the value 202311L in c++26"
117117
# endif
118118

119-
# if !defined(_LIBCPP_VERSION)
120-
# ifndef __cpp_lib_span_initializer_list
121-
# error "__cpp_lib_span_initializer_list should be defined in c++26"
122-
# endif
123-
# if __cpp_lib_span_initializer_list != 202311L
124-
# error "__cpp_lib_span_initializer_list should have the value 202311L in c++26"
125-
# endif
126-
# else // _LIBCPP_VERSION
127-
# ifdef __cpp_lib_span_initializer_list
128-
# error "__cpp_lib_span_initializer_list should not be defined because it is unimplemented in libc++!"
129-
# endif
119+
# ifndef __cpp_lib_span_initializer_list
120+
# error "__cpp_lib_span_initializer_list should be defined in c++26"
121+
# endif
122+
# if __cpp_lib_span_initializer_list != 202311L
123+
# error "__cpp_lib_span_initializer_list should have the value 202311L in c++26"
130124
# endif
131125

132126
#endif // TEST_STD_VER > 23

libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7294,17 +7294,11 @@
72947294
# error "__cpp_lib_span_at should have the value 202311L in c++26"
72957295
# endif
72967296

7297-
# if !defined(_LIBCPP_VERSION)
7298-
# ifndef __cpp_lib_span_initializer_list
7299-
# error "__cpp_lib_span_initializer_list should be defined in c++26"
7300-
# endif
7301-
# if __cpp_lib_span_initializer_list != 202311L
7302-
# error "__cpp_lib_span_initializer_list should have the value 202311L in c++26"
7303-
# endif
7304-
# else // _LIBCPP_VERSION
7305-
# ifdef __cpp_lib_span_initializer_list
7306-
# error "__cpp_lib_span_initializer_list should not be defined because it is unimplemented in libc++!"
7307-
# endif
7297+
# ifndef __cpp_lib_span_initializer_list
7298+
# error "__cpp_lib_span_initializer_list should be defined in c++26"
7299+
# endif
7300+
# if __cpp_lib_span_initializer_list != 202311L
7301+
# error "__cpp_lib_span_initializer_list should have the value 202311L in c++26"
73087302
# endif
73097303

73107304
# if !defined(_LIBCPP_VERSION)

0 commit comments

Comments
 (0)