[libcxx] [flang] [libcxxabi] [clang-tools-extra] [libunwind] [lldb] [clang] [compiler-rt] [llvm] [libc] [libc++][span] P2447R4: `std::span` over an initializer list (PR #78157)

Hristo Hristov via cfe-commits cfe-commits at lists.llvm.org
Thu Jan 18 10:33:41 PST 2024


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/78157

>From 01f0ed005f2037fa4a4ec64ad5e1a114da1f5e99 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 780d8574947590845ebc7fdb8c4097af32b87d09 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 cbe6fa053879200e497c22455f0f09915f605c27 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 de98ad0c99cedd21760b05782bbaf85e96a052c4 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 7add5b4fe4d1528651b35d37cd4797412bbc3f53 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 801cb187d9c9bfd7538eaa28cc958343148f0f0d 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 2383ac32f84e5592ab2454f906fbd8e7b5bc13a0 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 6c89879ca378b3277beb05b3c9dd96986528cb1d 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 2172198b2204f2e89bdc318d8a78d2d789ce7446 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..74a5094f61261d 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;
 }
 
+// Test 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_P2447R4_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_P2447R4_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..93bcd0551cdb05
--- /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"
+
+// Test 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_P2447R4_annex_c_examples() {
+  // 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 cfe-commits mailing list