[libcxx-commits] [libcxx] [libc++][span] P2447R4: `std::span` over an initializer list (PR #78157)

via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jan 15 05:17:41 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

<details>
<summary>Changes</summary>

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

---
Full diff: https://github.com/llvm/llvm-project/pull/78157.diff


12 Files Affected:

- (modified) libcxx/docs/FeatureTestMacroTable.rst (+1-1) 
- (modified) libcxx/docs/ReleaseNotes/18.rst (+1) 
- (modified) libcxx/docs/Status/Cxx2cPapers.csv (+1-1) 
- (modified) libcxx/include/span (+15) 
- (modified) libcxx/include/version (+1-1) 
- (modified) libcxx/test/std/containers/views/views.span/span.cons/array.pass.cpp (+4) 
- (added) libcxx/test/std/containers/views/views.span/span.cons/initializer_list.assert.pass.cpp (+38) 
- (modified) libcxx/test/std/containers/views/views.span/span.cons/initializer_list.pass.cpp (+57-15) 
- (modified) libcxx/test/std/containers/views/views.span/span.cons/iterator_len.verify.cpp (+19-5) 
- (modified) libcxx/test/std/language.support/support.limits/support.limits.general/span.version.compile.pass.cpp (+5-11) 
- (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+5-11) 
- (modified) libcxx/utils/generate_feature_test_macro_components.py (-1) 


``````````diff
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/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..9e5ef779bcadec 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,14 @@ 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()} {
+    _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 +406,12 @@ 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()} {}
+#  endif
+
   constexpr span(const span&) noexcept            = default;
   constexpr span& operator=(const span&) noexcept = default;
 
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/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..a078a43d6a6eee
--- /dev/null
+++ b/libcxx/test/std/containers/views/views.span/span.cons/initializer_list.assert.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <initializer_list>
+#include <span>
+
+#include "check_assertion.h"
+
+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().");
+
+  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..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
@@ -9,35 +9,77 @@
 
 // <span>
 
-#include <span>
+// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
+
 #include <cassert>
 #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 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*) {}
+  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(std::initializer_list<Sink>{a[0], a[1], a[2], a[3]}) == 4);
+  }
+#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());
 
-    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/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)
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",

``````````

</details>


https://github.com/llvm/llvm-project/pull/78157


More information about the libcxx-commits mailing list