[libcxx-commits] [libcxx] [libc++][span] P2447R4: `std::span` over an initializer list (PR #78157)
Hristo Hristov via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jan 15 13:54:27 PST 2024
https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/78157
>From fda50b67bf942b741e361f1fa5786727bc065084 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Thu, 11 Jan 2024 10:42:55 +0200
Subject: [PATCH 1/9] [libc++][span] P2447R4: `std::span` over an initializer
list
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
---
libcxx/docs/ReleaseNotes/18.rst | 1 +
libcxx/docs/Status/Cxx2cPapers.csv | 2 +-
libcxx/include/span | 18 +++
.../views/views.span/span.cons/array.pass.cpp | 4 +
.../initializer_list.assert.pass.cpp | 45 ++++++
.../span.cons/initializer_list.pass.cpp | 131 ++++++++++++++++--
.../span.cons/iterator_len.verify.cpp | 24 +++-
.../generate_feature_test_macro_components.py | 1 -
8 files changed, 204 insertions(+), 22 deletions(-)
create mode 100644 libcxx/test/std/containers/views/views.span/span.cons/initializer_list.assert.pass.cpp
diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index 62a1fec627d0ca..bf0ef8d447a3a5 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -60,6 +60,7 @@ Implemented Papers
- P0521R0 - Proposed Resolution for CA 14 (``shared_ptr`` ``use_count/unique``)
- P1759R6 - Native handles and file streams
- P2517R1 - Add a conditional ``noexcept`` specification to ``std::apply``
+- P2447R6 - ``span`` over initializer list
Improvements and New Features
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 5701717f39766c..2a7ee46816e9d9 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -34,7 +34,7 @@
"`P2918R2 <https://wg21.link/P2918R2>`__","LWG","Runtime format strings II","Kona November 2023","|Complete|","18.0","|format|"
"`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|"
"`P0952R2 <https://wg21.link/P0952R2>`__","LWG","A new specification for ``std::generate_canonical``","Kona November 2023","","",""
-"`P2447R6 <https://wg21.link/P2447R6>`__","LWG","``std::span`` over an initializer list","Kona November 2023","","",""
+"`P2447R6 <https://wg21.link/P2447R6>`__","LWG","``std::span`` over an initializer list","Kona November 2023","|Complete","18.0",""
"`P2821R5 <https://wg21.link/P2821R5>`__","LWG","``span.at()``","Kona November 2023","|Complete|","18.0",""
"`P2868R3 <https://wg21.link/P2868R3>`__","LWG","Remove Deprecated ``std::allocator`` Typedef From C++26","Kona November 2023","","",""
"`P2870R3 <https://wg21.link/P2870R3>`__","LWG","Remove ``basic_string::reserve()`` From C++26","Kona November 2023","|Complete|","18.0",""
diff --git a/libcxx/include/span b/libcxx/include/span
index 007a32597f965b..7ce0faa9457099 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -68,6 +68,7 @@ public:
constexpr span(const array<value_type, N>& arr) noexcept;
template<class R>
constexpr explicit(Extent != dynamic_extent) span(R&& r);
+ constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
constexpr span(const span& other) noexcept = default;
template <class OtherElementType, size_t OtherExtent>
constexpr explicit(Extent != dynamic_extent) span(const span<OtherElementType, OtherExtent>& s) noexcept;
@@ -228,6 +229,15 @@ public:
requires(_Sz == 0)
_LIBCPP_HIDE_FROM_ABI constexpr span() noexcept : __data_{nullptr} {}
+# if _LIBCPP_STD_VER >= 26
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit span(std::initializer_list<value_type> __il)
+ requires is_const_v<element_type>
+ : __data_{__il.begin()} {
+ // static_assert(false, "constructor (static extent) called");
+ _LIBCPP_ASSERT_INTERNAL(_Extent == __il.size(), "Size mismatch in span's constructor _Extent != __il.size().");
+ }
+# endif
+
constexpr span(const span&) noexcept = default;
constexpr span& operator=(const span&) noexcept = default;
@@ -397,6 +407,14 @@ public:
// [span.cons], span constructors, copy, assignment, and destructor
_LIBCPP_HIDE_FROM_ABI constexpr span() noexcept : __data_{nullptr}, __size_{0} {}
+# if _LIBCPP_STD_VER >= 26
+ _LIBCPP_HIDE_FROM_ABI constexpr span(std::initializer_list<value_type> __il)
+ requires is_const_v<element_type>
+ : __data_{__il.begin()}, __size_{__il.size()} {
+ // static_assert(false, "constructor (dynamic extent) called");
+ }
+# endif
+
constexpr span(const span&) noexcept = default;
constexpr span& operator=(const span&) noexcept = default;
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/array.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/array.pass.cpp
index 8fa7692c3b6370..b01fdda84789ce 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/array.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/array.pass.cpp
@@ -94,7 +94,11 @@ constexpr bool testSpan()
assert(s4.data() == val && s4.size() == 2);
std::span<const int> s5 = {{1,2}};
+#if TEST_STD_VER >= 26
+ std::span<const int, 2> s6({1,2});
+#else
std::span<const int, 2> s6 = {{1,2}};
+#endif
assert(s5.size() == 2); // and it dangles
assert(s6.size() == 2); // and it dangles
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.assert.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.assert.pass.cpp
new file mode 100644
index 00000000000000..ecf3f00e833e51
--- /dev/null
+++ b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.assert.pass.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: availability-verbose_abort-missing
+
+// <span>
+
+// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
+
+#include <cassert>
+#include <span>
+#include <initializer_list>
+
+#include "check_assertion.h"
+
+// template <std::size_t N>
+// void test_runtime_assertion(std::span<const int, N> sp) {
+// static_assert(std::dynamic_extent != sp.extent);
+// }
+
+bool test() {
+ TEST_LIBCPP_ASSERT_FAILURE(
+ (std::span<const int, 4>({1, 2, 3, 9084, 5})), "Size mismatch in span's constructor _Extent != __il.size().");
+ TEST_LIBCPP_ASSERT_FAILURE((std::span<const int, 4>(std::initializer_list<int>{1, 2, 3, 9084, 5})),
+ "Size mismatch in span's constructor _Extent != __il.size().");
+ // TEST_LIBCPP_ASSERT_FAILURE((test_runtime_assertion(std::span<const int, 4>({1, 2, 3, 9084, 0}))),
+ // "Size mismatch in span's constructor _Extent != __il.size().");
+
+ return true;
+}
+
+int main(int, char**) {
+ assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
index d0f4cc795f3e27..a06cecba7b55b7 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
@@ -9,35 +9,136 @@
// <span>
-#include <span>
+// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
+
+#include <array>
#include <cassert>
+#include <concepts>
#include <cstddef>
+#include <initializer_list>
+#include <span>
+#include <vector>
+
+#include "test_macros.h"
+
+#include <print>
+
+#if TEST_STD_VER >= 26
+
+constexpr int kEmptySpanValue = -1;
+
+template <std::size_t N>
+int take_last_element(std::span<const int, N> sp, std::size_t expectedSize) {
+ static_assert(std::dynamic_extent != sp.extent);
+ assert(sp.size() == expectedSize);
+ if (sp.size() == 0)
+ return kEmptySpanValue;
+ return sp.at(sp.size() - 1);
+}
+
+int take_last_element(std::span<const int> sp, std::size_t expectedSize) {
+ static_assert(std::dynamic_extent == sp.extent);
+ std::println(stderr, "size ----- {}", sp.size());
+ assert(sp.size() == expectedSize);
+ if (sp.size() == 0)
+ return kEmptySpanValue;
+ return sp.at(sp.size() - 1);
+}
+
+bool test_span() {
+ // Static
+ // {
+ // int lastElem = take_last_element<0>({{}}, 0);
+ // assert(lastElem == kEmptySpanValue);
+ // }
+ // {
+ // int lastElem = take_last_element<1>({{1}}, 1);
+ // assert(lastElem == kEmptySpanValue);
+ // }
+ // {
+ // int lastElem = take_last_element<4>(std::array{1, 2, 3, 9084}, 4);
+ // assert(lastElem == 9084);
+ // }
+ // {
+ // int lastElem = take_last_element<4>(std::initializer_list<int>{1, 2, 3, 9084}, 4);
+ // assert(lastElem == 9084);
+ // }
+ // std::span<const int, 4>({1, 2, 3, 9084, 5});
+ // {
+ // int lastElem = take_last_element(std::span<const int, 4>({1, 2, 3, 9084}), 4);
+ // assert(lastElem == 9084);
+ // }
+ // Dynamic
+ // {
+ // int lastElem = take_last_element({{}}, 1);
+ // assert(lastElem == kEmptySpanValue);
+ // }
+ {
+ int lastElem = take_last_element({{1, 2, 3, 9084}}, 4);
+ assert(lastElem == 9084);
+ }
+ // {
+ // int lastElem = take_last_element(std::vector{1, 2, 3, 9084}, 4);
+ // assert(lastElem == 9084);
+ // }
+ {
+ int lastElem = take_last_element(std::initializer_list<int>{1, 2, 3, 9084}, 4);
+ assert(lastElem == 9084);
+ }
+ {
+ int lastElem = take_last_element(std::span<const int>({1, 2, 3, 9084}), 4);
+ assert(lastElem == 9084);
+ }
+
+ return true;
+}
+
+#endif
struct Sink {
- constexpr Sink() = default;
- constexpr Sink(Sink*) {}
+ constexpr Sink() = default;
+ constexpr Sink(Sink*) {}
};
-constexpr std::size_t count(std::span<const Sink> sp) {
- return sp.size();
-}
+constexpr std::size_t count(std::span<const Sink> sp) { return sp.size(); }
-template<int N>
-constexpr std::size_t countn(std::span<const Sink, N> sp) {
- return sp.size();
+template <std::size_t N>
+constexpr std::size_t count_n(std::span<const Sink, N> sp) {
+ return sp.size();
}
constexpr bool test() {
+#if TEST_STD_VER >= 26
+ // Dynamic extent
+ {
Sink a[10];
+
+ assert(count({a}) == 1);
+ assert(count({a, a + 10}) == 2);
+ assert(count({a, a + 1, a + 2}) == 3);
+ // assert(count_n<3>(std::array{a, a + 1, a + 2}) == 3);
+ // assert(count_n<3>(std::array{a, a + 1, a + 2, a + 3, a + 4}) == 3);
+ }
+#else
+ {
+ Sink a[10];
+
assert(count({a}) == 10);
- assert(count({a, a+10}) == 10);
- assert(countn<10>({a}) == 10);
- return true;
+ assert(count({a, a + 10}) == 10);
+ assert(count_n<10>({a}) == 10);
+ }
+#endif
+
+ return true;
}
int main(int, char**) {
- test();
- static_assert(test());
+ test();
+ static_assert(test());
+
+#if TEST_STD_VER >= 26
+ test_span();
+#endif
- return 0;
+ return 0;
}
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/iterator_len.verify.cpp b/libcxx/test/std/containers/views/views.span/span.cons/iterator_len.verify.cpp
index 3836c97e94c6d6..112057e258be33 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/iterator_len.verify.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/iterator_len.verify.cpp
@@ -13,22 +13,36 @@
// constexpr explicit(Extent != dynamic_extent) span(It first, size_type count);
// If Extent is not equal to dynamic_extent, then count shall be equal to Extent.
//
+// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
#include <span>
#include <cstddef>
+#include "test_macros.h"
+
template <class T, std::size_t extent>
std::span<T, extent> createImplicitSpan(T* ptr, std::size_t len) {
return {ptr, len}; // expected-error {{chosen constructor is explicit in copy-initialization}}
}
-void f() {
+void test() {
// explicit constructor necessary
int arr[] = {1, 2, 3};
createImplicitSpan<int, 1>(arr, 3);
- std::span<int> sp = {0, 0}; // expected-error {{no matching constructor for initialization of 'std::span<int>'}}
- std::span<int, 2> sp2 = {0, 0}; // expected-error {{no matching constructor for initialization of 'std::span<int, 2>'}}
- std::span<const int> csp = {0, 0}; // expected-error {{no matching constructor for initialization of 'std::span<const int>'}}
- std::span<const int, 2> csp2 = {0, 0}; // expected-error {{no matching constructor for initialization of 'std::span<const int, 2>'}}
+ // expected-error at +1 {{no matching constructor for initialization of 'std::span<int>'}}
+ std::span<int> sp = {0, 0};
+ // expected-error at +1 {{no matching constructor for initialization of 'std::span<int, 2>'}}
+ std::span<int, 2> sp2 = {0, 0};
+#if TEST_STD_VER >= 26
+ // No error in C++26
+ std::span<const int> csp = {0, 0};
+ // expected-error at +1 {{chosen constructor is explicit in copy-initialization}}
+ std::span<const int, 2> csp2 = {0, 0};
+#else
+ // expected-error at +1 {{no matching constructor for initialization of 'std::span<const int>'}}
+ std::span<const int> csp = {0, 0};
+ // expected-error at +1 {{no matching constructor for initialization of 'std::span<const int, 2>'}}
+ std::span<const int, 2> csp2 = {0, 0};
+#endif
}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 8ee92909dfa53c..958c34edde0059 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1093,7 +1093,6 @@ def add_version_header(tc):
"name": "__cpp_lib_span_initializer_list",
"values": {"c++26": 202311}, # P2447R6 std::span over an initializer list
"headers": ["span"],
- "unimplemented": True,
},
{
"name": "__cpp_lib_spanstream",
>From bcdb3caea7eaeb357bca0bbaa291e3647ad6bf4e Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Mon, 15 Jan 2024 12:44:48 +0200
Subject: [PATCH 2/9] WIP: Cleanup
---
libcxx/include/span | 5 +-
.../initializer_list.assert.pass.cpp | 9 +-
.../span.cons/initializer_list.pass.cpp | 83 +------------------
3 files changed, 3 insertions(+), 94 deletions(-)
diff --git a/libcxx/include/span b/libcxx/include/span
index 7ce0faa9457099..9e5ef779bcadec 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -233,7 +233,6 @@ public:
_LIBCPP_HIDE_FROM_ABI constexpr explicit span(std::initializer_list<value_type> __il)
requires is_const_v<element_type>
: __data_{__il.begin()} {
- // static_assert(false, "constructor (static extent) called");
_LIBCPP_ASSERT_INTERNAL(_Extent == __il.size(), "Size mismatch in span's constructor _Extent != __il.size().");
}
# endif
@@ -410,9 +409,7 @@ public:
# if _LIBCPP_STD_VER >= 26
_LIBCPP_HIDE_FROM_ABI constexpr span(std::initializer_list<value_type> __il)
requires is_const_v<element_type>
- : __data_{__il.begin()}, __size_{__il.size()} {
- // static_assert(false, "constructor (dynamic extent) called");
- }
+ : __data_{__il.begin()}, __size_{__il.size()} {}
# endif
constexpr span(const span&) noexcept = default;
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.assert.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.assert.pass.cpp
index ecf3f00e833e51..a078a43d6a6eee 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.assert.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.assert.pass.cpp
@@ -17,23 +17,16 @@
// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
#include <cassert>
-#include <span>
#include <initializer_list>
+#include <span>
#include "check_assertion.h"
-// template <std::size_t N>
-// void test_runtime_assertion(std::span<const int, N> sp) {
-// static_assert(std::dynamic_extent != sp.extent);
-// }
-
bool test() {
TEST_LIBCPP_ASSERT_FAILURE(
(std::span<const int, 4>({1, 2, 3, 9084, 5})), "Size mismatch in span's constructor _Extent != __il.size().");
TEST_LIBCPP_ASSERT_FAILURE((std::span<const int, 4>(std::initializer_list<int>{1, 2, 3, 9084, 5})),
"Size mismatch in span's constructor _Extent != __il.size().");
- // TEST_LIBCPP_ASSERT_FAILURE((test_runtime_assertion(std::span<const int, 4>({1, 2, 3, 9084, 0}))),
- // "Size mismatch in span's constructor _Extent != __il.size().");
return true;
}
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
index a06cecba7b55b7..932b962feacd18 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
@@ -13,88 +13,12 @@
#include <array>
#include <cassert>
-#include <concepts>
#include <cstddef>
#include <initializer_list>
#include <span>
-#include <vector>
#include "test_macros.h"
-#include <print>
-
-#if TEST_STD_VER >= 26
-
-constexpr int kEmptySpanValue = -1;
-
-template <std::size_t N>
-int take_last_element(std::span<const int, N> sp, std::size_t expectedSize) {
- static_assert(std::dynamic_extent != sp.extent);
- assert(sp.size() == expectedSize);
- if (sp.size() == 0)
- return kEmptySpanValue;
- return sp.at(sp.size() - 1);
-}
-
-int take_last_element(std::span<const int> sp, std::size_t expectedSize) {
- static_assert(std::dynamic_extent == sp.extent);
- std::println(stderr, "size ----- {}", sp.size());
- assert(sp.size() == expectedSize);
- if (sp.size() == 0)
- return kEmptySpanValue;
- return sp.at(sp.size() - 1);
-}
-
-bool test_span() {
- // Static
- // {
- // int lastElem = take_last_element<0>({{}}, 0);
- // assert(lastElem == kEmptySpanValue);
- // }
- // {
- // int lastElem = take_last_element<1>({{1}}, 1);
- // assert(lastElem == kEmptySpanValue);
- // }
- // {
- // int lastElem = take_last_element<4>(std::array{1, 2, 3, 9084}, 4);
- // assert(lastElem == 9084);
- // }
- // {
- // int lastElem = take_last_element<4>(std::initializer_list<int>{1, 2, 3, 9084}, 4);
- // assert(lastElem == 9084);
- // }
- // std::span<const int, 4>({1, 2, 3, 9084, 5});
- // {
- // int lastElem = take_last_element(std::span<const int, 4>({1, 2, 3, 9084}), 4);
- // assert(lastElem == 9084);
- // }
- // Dynamic
- // {
- // int lastElem = take_last_element({{}}, 1);
- // assert(lastElem == kEmptySpanValue);
- // }
- {
- int lastElem = take_last_element({{1, 2, 3, 9084}}, 4);
- assert(lastElem == 9084);
- }
- // {
- // int lastElem = take_last_element(std::vector{1, 2, 3, 9084}, 4);
- // assert(lastElem == 9084);
- // }
- {
- int lastElem = take_last_element(std::initializer_list<int>{1, 2, 3, 9084}, 4);
- assert(lastElem == 9084);
- }
- {
- int lastElem = take_last_element(std::span<const int>({1, 2, 3, 9084}), 4);
- assert(lastElem == 9084);
- }
-
- return true;
-}
-
-#endif
-
struct Sink {
constexpr Sink() = default;
constexpr Sink(Sink*) {}
@@ -116,8 +40,7 @@ constexpr bool test() {
assert(count({a}) == 1);
assert(count({a, a + 10}) == 2);
assert(count({a, a + 1, a + 2}) == 3);
- // assert(count_n<3>(std::array{a, a + 1, a + 2}) == 3);
- // assert(count_n<3>(std::array{a, a + 1, a + 2, a + 3, a + 4}) == 3);
+ assert(count(std::initializer_list<Sink>{a[0], a[1], a[2], a[3]}) == 4);
}
#else
{
@@ -136,9 +59,5 @@ int main(int, char**) {
test();
static_assert(test());
-#if TEST_STD_VER >= 26
- test_span();
-#endif
-
return 0;
}
>From 9f2af2390a7cf0856c604c328a52e3e2c1282ea5 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Mon, 15 Jan 2024 12:48:20 +0200
Subject: [PATCH 3/9] Added generated version files
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +-
libcxx/include/version | 2 +-
.../span.version.compile.pass.cpp | 16 +++++-----------
.../version.version.compile.pass.cpp | 16 +++++-----------
4 files changed, 12 insertions(+), 24 deletions(-)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 893a3b13ca06e0..82e51d4e7eb53d 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -438,7 +438,7 @@ Status
--------------------------------------------------- -----------------
``__cpp_lib_span_at`` ``202311L``
--------------------------------------------------- -----------------
- ``__cpp_lib_span_initializer_list`` *unimplemented*
+ ``__cpp_lib_span_initializer_list`` ``202311L``
--------------------------------------------------- -----------------
``__cpp_lib_sstream_from_string_view`` *unimplemented*
--------------------------------------------------- -----------------
diff --git a/libcxx/include/version b/libcxx/include/version
index c96647894dce63..7b2f487fe70205 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -507,7 +507,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
// # define __cpp_lib_saturation_arithmetic 202311L
// # define __cpp_lib_smart_ptr_owner_equality 202306L
# define __cpp_lib_span_at 202311L
-// # define __cpp_lib_span_initializer_list 202311L
+# define __cpp_lib_span_initializer_list 202311L
// # define __cpp_lib_sstream_from_string_view 202306L
// # define __cpp_lib_submdspan 202306L
// # define __cpp_lib_text_encoding 202306L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/span.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/span.version.compile.pass.cpp
index dbbbaf4ec7c228..e1694308f12a7b 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/span.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/span.version.compile.pass.cpp
@@ -116,17 +116,11 @@
# error "__cpp_lib_span_at should have the value 202311L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_span_initializer_list
-# error "__cpp_lib_span_initializer_list should be defined in c++26"
-# endif
-# if __cpp_lib_span_initializer_list != 202311L
-# error "__cpp_lib_span_initializer_list should have the value 202311L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_span_initializer_list
-# error "__cpp_lib_span_initializer_list should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_span_initializer_list
+# error "__cpp_lib_span_initializer_list should be defined in c++26"
+# endif
+# if __cpp_lib_span_initializer_list != 202311L
+# error "__cpp_lib_span_initializer_list should have the value 202311L in c++26"
# endif
#endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index d5a0839b30f824..fa188533446b47 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -7294,17 +7294,11 @@
# error "__cpp_lib_span_at should have the value 202311L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_span_initializer_list
-# error "__cpp_lib_span_initializer_list should be defined in c++26"
-# endif
-# if __cpp_lib_span_initializer_list != 202311L
-# error "__cpp_lib_span_initializer_list should have the value 202311L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_span_initializer_list
-# error "__cpp_lib_span_initializer_list should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_span_initializer_list
+# error "__cpp_lib_span_initializer_list should be defined in c++26"
+# endif
+# if __cpp_lib_span_initializer_list != 202311L
+# error "__cpp_lib_span_initializer_list should have the value 202311L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
>From 2dd57d2f4e828f24845d1bbad75c8c3b7f1674f1 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Mon, 15 Jan 2024 13:26:39 +0200
Subject: [PATCH 4/9] Added SFINAE
---
.../span.cons/initializer_list.pass.cpp | 29 +++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
index 932b962feacd18..66f4de5eb3ab4e 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
@@ -16,9 +16,38 @@
#include <cstddef>
#include <initializer_list>
#include <span>
+#include <type_traits>
#include "test_macros.h"
+#if TEST_STD_VER >= 26
+
+// SFINAE
+
+template <typename T>
+concept ConstElementType = std::is_const_v<typename T::element_type>;
+
+static_assert(ConstElementType<std::span<const int>>);
+static_assert(!ConstElementType<std::span<int>>);
+static_assert(ConstElementType<std::span<const int, 94>>);
+static_assert(!ConstElementType<std::span<int, 94>>);
+
+// template <typename T, typename I>
+// concept HasInitializerListCtr = requires(I il) { std::span<T>{il}; };
+
+// static_assert(HasInitializerListCtr<const int, std::initializer_list<const int>>);
+// static_assert(!HasInitializerListCtr<int, std::initializer_list<int>>);
+
+template <typename I, typename T, std::size_t... N>
+concept HasInitializerListCtr = requires(I il) { std::span<T, N...>{il}; };
+
+static_assert(HasInitializerListCtr<std::initializer_list<const int>, const int>);
+static_assert(!HasInitializerListCtr<std::initializer_list<int>, int>);
+static_assert(HasInitializerListCtr<std::initializer_list<const int>, const int, 94>);
+static_assert(!HasInitializerListCtr<std::initializer_list<int>, int, 94>);
+
+#endif
+
struct Sink {
constexpr Sink() = default;
constexpr Sink(Sink*) {}
>From a5a4a39b6a4bdd2cd66db3cff2932cc4e49e524f Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Mon, 15 Jan 2024 15:15:55 +0200
Subject: [PATCH 5/9] Cleanup
---
.../views/views.span/span.cons/initializer_list.pass.cpp | 7 -------
1 file changed, 7 deletions(-)
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
index 66f4de5eb3ab4e..174a45ee6129c6 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
@@ -11,7 +11,6 @@
// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
-#include <array>
#include <cassert>
#include <cstddef>
#include <initializer_list>
@@ -32,12 +31,6 @@ static_assert(!ConstElementType<std::span<int>>);
static_assert(ConstElementType<std::span<const int, 94>>);
static_assert(!ConstElementType<std::span<int, 94>>);
-// template <typename T, typename I>
-// concept HasInitializerListCtr = requires(I il) { std::span<T>{il}; };
-
-// static_assert(HasInitializerListCtr<const int, std::initializer_list<const int>>);
-// static_assert(!HasInitializerListCtr<int, std::initializer_list<int>>);
-
template <typename I, typename T, std::size_t... N>
concept HasInitializerListCtr = requires(I il) { std::span<T, N...>{il}; };
>From 880749e4bda0291cf0b4d55fe9481c18d46413ec Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Mon, 15 Jan 2024 15:21:06 +0200
Subject: [PATCH 6/9] Try to fix CI
---
libcxx/docs/Status/Cxx2cPapers.csv | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 2a7ee46816e9d9..e48e2e94b30582 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -34,7 +34,7 @@
"`P2918R2 <https://wg21.link/P2918R2>`__","LWG","Runtime format strings II","Kona November 2023","|Complete|","18.0","|format|"
"`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|"
"`P0952R2 <https://wg21.link/P0952R2>`__","LWG","A new specification for ``std::generate_canonical``","Kona November 2023","","",""
-"`P2447R6 <https://wg21.link/P2447R6>`__","LWG","``std::span`` over an initializer list","Kona November 2023","|Complete","18.0",""
+"`P2447R6 <https://wg21.link/P2447R6>`__","LWG","``std::span`` over an initializer list","Kona November 2023","|Complete|","18.0",""
"`P2821R5 <https://wg21.link/P2821R5>`__","LWG","``span.at()``","Kona November 2023","|Complete|","18.0",""
"`P2868R3 <https://wg21.link/P2868R3>`__","LWG","Remove Deprecated ``std::allocator`` Typedef From C++26","Kona November 2023","","",""
"`P2870R3 <https://wg21.link/P2870R3>`__","LWG","Remove ``basic_string::reserve()`` From C++26","Kona November 2023","|Complete|","18.0",""
>From ba58c20606f1d83e90ad6db5c8b50fc5fed2a0e5 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Mon, 15 Jan 2024 15:22:35 +0200
Subject: [PATCH 7/9] Try to fix CI
---
.../std/containers/views/views.span/span.cons/array.pass.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/array.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/array.pass.cpp
index b01fdda84789ce..c02f42400b6e1e 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/array.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/array.pass.cpp
@@ -95,7 +95,7 @@ constexpr bool testSpan()
std::span<const int> s5 = {{1,2}};
#if TEST_STD_VER >= 26
- std::span<const int, 2> s6({1,2});
+ std::span<const int, 2> s6({1, 2});
#else
std::span<const int, 2> s6 = {{1,2}};
#endif
>From 89ecbe9bf2cf33d63d277ec14f5040d3d3c786bb Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Mon, 15 Jan 2024 22:47:31 +0200
Subject: [PATCH 8/9] WIP: Addressed some comments
---
libcxx/include/span | 3 ++-
.../views.span/span.cons/initializer_list.pass.cpp | 12 ++++++++++++
.../views.span/span.cons/iterator_len.verify.cpp | 7 +------
3 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/libcxx/include/span b/libcxx/include/span
index 9e5ef779bcadec..32364b4270be9e 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -233,7 +233,8 @@ public:
_LIBCPP_HIDE_FROM_ABI constexpr explicit span(std::initializer_list<value_type> __il)
requires is_const_v<element_type>
: __data_{__il.begin()} {
- _LIBCPP_ASSERT_INTERNAL(_Extent == __il.size(), "Size mismatch in span's constructor _Extent != __il.size().");
+ _LIBCPP_ASSERT_VALID_INPUT_RANGE(
+ _Extent == __il.size(), "Size mismatch in span's constructor _Extent != __il.size().");
}
# endif
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
index 174a45ee6129c6..b1453aae649792 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
@@ -17,6 +17,7 @@
#include <span>
#include <type_traits>
+#include "test_convertible.h"
#include "test_macros.h"
#if TEST_STD_VER >= 26
@@ -31,6 +32,8 @@ static_assert(!ConstElementType<std::span<int>>);
static_assert(ConstElementType<std::span<const int, 94>>);
static_assert(!ConstElementType<std::span<int, 94>>);
+// Constructor constraings
+
template <typename I, typename T, std::size_t... N>
concept HasInitializerListCtr = requires(I il) { std::span<T, N...>{il}; };
@@ -39,6 +42,15 @@ static_assert(!HasInitializerListCtr<std::initializer_list<int>, int>);
static_assert(HasInitializerListCtr<std::initializer_list<const int>, const int, 94>);
static_assert(!HasInitializerListCtr<std::initializer_list<int>, int, 94>);
+// Constructor conditionally explicit
+
+static_assert(!test_convertible<std::span<const int, 28>, std::initializer_list<int>>(),
+ "This constructor must be explicit");
+static_assert(std::is_constructible_v<std::span<const int, 28>, std::initializer_list<int>>);
+static_assert(test_convertible<std::span<const int>, std::initializer_list<int>>(),
+ "This constructor must not be explicit");
+static_assert(std::is_constructible_v<std::span<const int>, std::initializer_list<int>>);
+
#endif
struct Sink {
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/iterator_len.verify.cpp b/libcxx/test/std/containers/views/views.span/span.cons/iterator_len.verify.cpp
index 112057e258be33..176ca200792695 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/iterator_len.verify.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/iterator_len.verify.cpp
@@ -34,12 +34,7 @@ void test() {
std::span<int> sp = {0, 0};
// expected-error at +1 {{no matching constructor for initialization of 'std::span<int, 2>'}}
std::span<int, 2> sp2 = {0, 0};
-#if TEST_STD_VER >= 26
- // No error in C++26
- std::span<const int> csp = {0, 0};
- // expected-error at +1 {{chosen constructor is explicit in copy-initialization}}
- std::span<const int, 2> csp2 = {0, 0};
-#else
+#if TEST_STD_VER < 26
// expected-error at +1 {{no matching constructor for initialization of 'std::span<const int>'}}
std::span<const int> csp = {0, 0};
// expected-error at +1 {{no matching constructor for initialization of 'std::span<const int, 2>'}}
>From 0f5e02bfcfa67b21d61c1c9e5fded27943b03f33 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Mon, 15 Jan 2024 23:52:49 +0200
Subject: [PATCH 9/9] Addressed comments
---
.../span.cons/initializer_list.pass.cpp | 43 ++++++++++++++++-
.../span.cons/initializer_list.verify.cpp | 48 +++++++++++++++++++
2 files changed, 90 insertions(+), 1 deletion(-)
create mode 100644 libcxx/test/std/containers/views/views.span/span.cons/initializer_list.verify.cpp
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
index b1453aae649792..5aa83c6c4295c3 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp
@@ -5,12 +5,14 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+
// UNSUPPORTED: c++03, c++11, c++14, c++17
// <span>
// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
+#include <any>
#include <cassert>
#include <cstddef>
#include <initializer_list>
@@ -89,9 +91,48 @@ constexpr bool test() {
return true;
}
+// Tests P2447R4 "Annex C examples"
+
+constexpr int three(std::span<void* const> sp) { return sp.size(); }
+
+constexpr int four(std::span<const std::any> sp) { return sp.size(); }
+
+bool test_annex_c_examples() {
+ // 1. Overload resolution is affected
+ // --> tested in "initializer_list.verify.cpp"
+
+ // 2. The `initializer_list` ctor has high precedence
+ // --> tested in "initializer_list.verify.cpp"
+
+ // 3. Implicit two-argument construction with a highly convertible value_type
+#if TEST_STD_VER >= 26
+ {
+ void* a[10];
+ assert(three({a, 0}) == 2);
+ }
+ {
+ std::any a[10];
+ assert(four({a, a + 10}) == 2);
+ }
+#else
+ {
+ void* a[10];
+ assert(three({a, 0}) == 0);
+ }
+ {
+ std::any a[10];
+ assert(four({a, a + 10}) == 10);
+ }
+#endif
+
+ return true;
+}
+
int main(int, char**) {
- test();
+ assert(test());
static_assert(test());
+ assert(test_annex_c_examples());
+
return 0;
}
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.verify.cpp b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.verify.cpp
new file mode 100644
index 00000000000000..c0bf6ab910e24d
--- /dev/null
+++ b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.verify.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <span>
+
+// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
+
+#include <span>
+#include <utility>
+
+#include "test_macros.h"
+
+// Tests P2447R4 "Annex C examples"
+
+void one(std::pair<int, int>);
+void one(std::span<const int>);
+
+void two(std::span<const int, 2>);
+
+void test() {
+ // 1. Overload resolution is affected
+#if TEST_STD_VER >= 26
+ // expected-error at +1 {{call to 'one' is ambiguous}}
+ one({1, 2});
+#else
+ // expected-no-diagnostics
+ one({1, 2});
+#endif
+
+// 2. The `initializer_list` ctor has high precedence
+#if TEST_STD_VER >= 26
+ // expected-error at +1 {{chosen constructor is explicit in copy-initialization}}
+ two({{1, 2}});
+#else
+ // expected-no-diagnostics
+ two({{1, 2}});
+#endif
+
+ // 3. Implicit two-argument construction with a highly convertible value_type
+ // --> tested in "initializer_list.pass.cpp"
+}
More information about the libcxx-commits
mailing list