Skip to content

[libc++][span] P2447R4: std::span over an initializer list #78157

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

H-G-Hristov
Copy link
Contributor

@H-G-Hristov H-G-Hristov requested a review from a team as a code owner January 15, 2024 13:17
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jan 15, 2024
@llvmbot
Copy link
Member

llvmbot commented Jan 15, 2024

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

Changes

Implements: https://wg21.link/P2447R6


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 --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@+1 {{no matching constructor for initialization of 'std::span<int>'}}
+  std::span<int> sp = {0, 0};
+  // expected-error@+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@+1 {{chosen constructor is explicit in copy-initialization}}
+  std::span<const int, 2> csp2 = {0, 0};
+#else
+  // expected-error@+1 {{no matching constructor for initialization of 'std::span<const int>'}}
+  std::span<const int> csp = {0, 0};
+  // expected-error@+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",

Copy link

github-actions bot commented Jan 15, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Member

@mordante mordante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly looks good, some minor issues.

@mordante mordante self-assigned this Jan 15, 2024
@H-G-Hristov H-G-Hristov force-pushed the hgh/libcxx/P2447R4-span-over-initializer_list branch 3 times, most recently from 998e512 to 0f5e02b Compare January 15, 2024 21:54
@H-G-Hristov H-G-Hristov force-pushed the hgh/libcxx/P2447R4-span-over-initializer_list branch from 0f5e02b to 2172198 Compare January 15, 2024 22:34
@H-G-Hristov
Copy link
Contributor Author

Thanks. The documentation build fails due to some CMake error consistently otherwise I believe I addressed all comments.

@H-G-Hristov H-G-Hristov requested a review from mordante January 16, 2024 07:44
Copy link
Member

@mordante mordante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. The documentation build fails due to some CMake error consistently otherwise I believe I addressed all comments.
I think I fixed this a few days ago.

LGTM modulo 1 nit.

}

int main(int, char**) {
assert(test());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We typically don't do this for run-time tests. We do it for compile-time tests so we can use static_asserts. In this case I would remove the test function and move the test code in main.

I overlooked this in the previous review.

Comment on lines +101 to +107
// 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice! thanks for referring to the other test too!

@H-G-Hristov
Copy link
Contributor Author

Thanks. The documentation build fails due to some CMake error consistently otherwise I believe I addressed all comments.
I think I fixed this a few days ago.

LGTM modulo 1 nit.

Thanx!

@Zingam Zingam merged commit dbbeee6 into llvm:main Jan 20, 2024
@H-G-Hristov H-G-Hristov deleted the hgh/libcxx/P2447R4-span-over-initializer_list branch January 20, 2024 06:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants