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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libcxx/docs/FeatureTestMacroTable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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*
--------------------------------------------------- -----------------
Expand Down
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes/18.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx2cPapers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -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",""
Expand Down
16 changes: 16 additions & 0 deletions libcxx/include/span
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()} {
_LIBCPP_ASSERT_VALID_INPUT_RANGE(
_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;

Expand Down Expand Up @@ -397,6 +407,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;

Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/version
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//===----------------------------------------------------------------------===//
//
// 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"

int main(int, char**) {
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 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,134 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17

// <span>

#include <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>
#include <span>
#include <type_traits>

#include "test_convertible.h"
#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>>);

// Constructor constraings

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>);

// 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 {
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;
}

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

#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();
static_assert(test());
assert(test());
static_assert(test());

assert(test_P2447R4_annex_c_examples());

return 0;
return 0;
}
Original file line number Diff line number Diff line change
@@ -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@+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@+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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,31 @@
// 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
// 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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading