On Wed, 11 Dec 2024 at 22:53, Jonathan Wakely <jwak...@redhat.com> wrote: > > On 03/12/24 17:05 +0100, Giuseppe D'Angelo wrote: > >Hello, > > > >The attached patch adds the span(initializer_list) constructor, added > >by P2447R6 for C++26. > > > >It seems that GCC is somewhat aggressive with its -Winit-list-lifetime > >warning, which actively interferes with this feature. The idea of the > >new constructor is to allow calls like: > > > > void f(std::span<const int>); > > f({1, 2, 3}); > > > >which is completely fine as the lifetime of the initializer_list > >encompasses the one of the std::span parameter. However GCC complains > >about the risk of dangling here. I've therefore disabled the warning > >for the new constructor. > > > >Thanks, > >-- > >Giuseppe D'Angelo > > >From bb1d537ee7b68883403127903834427c6787c505 Mon Sep 17 00:00:00 2001 > >From: Giuseppe D'Angelo <giuseppe.dang...@kdab.com> > >Date: Tue, 3 Dec 2024 16:56:45 +0100 > >Subject: [PATCH] libstdc++: add initializer_list constructor to std::span > > (P2447) > > > >This commit implements P2447R6. The code is straightforward (just one > >extra constructor, with constraints and conditional explicit). > > > >I decided to suppress -Winit-list-lifetime because otherwise it would > >give too many false positives. The new constructor is meant to be used > >as a parameter-passing interface (this is a design choice, see > >P2447R6/2) and, as such, the initializer_list won't dangle despite GCC's > >warnings. > > > >The new constructor isn't 100% backwards compatible. A couple of > >examples are included in Annex C, but I have also lifted some more > >from R4. A new test checks for the old and the new behaviors. > > > >libstdc++-v3/ChangeLog: > > > > * include/bits/version.def: Added the new feature-testing macro. > > * include/bits/version.h (defined): Regenerated. > > * include/std/span: Added constructor from initializer_list. > > * testsuite/23_containers/span/init_list_cons.cc: New test. > > * testsuite/23_containers/span/init_list_cons_neg.cc: New test. > >--- > > libstdc++-v3/include/bits/version.def | 8 +++ > > libstdc++-v3/include/bits/version.h | 10 +++ > > libstdc++-v3/include/std/span | 21 ++++++ > > .../23_containers/span/init_list_cons.cc | 65 +++++++++++++++++++ > > .../23_containers/span/init_list_cons_neg.cc | 31 +++++++++ > > 5 files changed, 135 insertions(+) > > create mode 100644 > > libstdc++-v3/testsuite/23_containers/span/init_list_cons.cc > > create mode 100644 > > libstdc++-v3/testsuite/23_containers/span/init_list_cons_neg.cc > > > >diff --git a/libstdc++-v3/include/bits/version.def > >b/libstdc++-v3/include/bits/version.def > >index 8d4b8e9b383..cfa0469fb2d 100644 > >--- a/libstdc++-v3/include/bits/version.def > >+++ b/libstdc++-v3/include/bits/version.def > >@@ -1853,6 +1853,14 @@ ftms = { > > }; > > }; > > > >+ftms = { > >+ name = span_initializer_list; > >+ values = { > >+ v = 202311; > >+ cxxmin = 26; > >+ }; > >+}; > >+ > > ftms = { > > name = text_encoding; > > values = { > >diff --git a/libstdc++-v3/include/bits/version.h > >b/libstdc++-v3/include/bits/version.h > >index c556aca38fa..6a2c66bdf81 100644 > >--- a/libstdc++-v3/include/bits/version.h > >+++ b/libstdc++-v3/include/bits/version.h > >@@ -2055,6 +2055,16 @@ > > #endif /* !defined(__cpp_lib_saturation_arithmetic) && > > defined(__glibcxx_want_saturation_arithmetic) */ > > #undef __glibcxx_want_saturation_arithmetic > > > >+#if !defined(__cpp_lib_span_initializer_list) > >+# if (__cplusplus > 202302L) > >+# define __glibcxx_span_initializer_list 202311L > >+# if defined(__glibcxx_want_all) || > >defined(__glibcxx_want_span_initializer_list) > >+# define __cpp_lib_span_initializer_list 202311L > >+# endif > >+# endif > >+#endif /* !defined(__cpp_lib_span_initializer_list) && > >defined(__glibcxx_want_span_initializer_list) */ > >+#undef __glibcxx_want_span_initializer_list > >+ > > #if !defined(__cpp_lib_text_encoding) > > # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && > > (_GLIBCXX_USE_NL_LANGINFO_L) > > # define __glibcxx_text_encoding 202306L > >diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span > >index f1c19b58737..b84ac87b657 100644 > >--- a/libstdc++-v3/include/std/span > >+++ b/libstdc++-v3/include/std/span > >@@ -39,6 +39,7 @@ > > #endif > > > > #define __glibcxx_want_span > >+#define __glibcxx_want_span_initializer_list > > #include <bits/version.h> > > > > #ifdef __cpp_lib_span // C++ >= 20 && concepts > >@@ -46,6 +47,9 @@ > > #include <cstddef> > > #include <bits/stl_iterator.h> > > #include <bits/ranges_base.h> > >+#ifdef __cpp_lib_span_initializer_list > >+# include <initializer_list> > >+#endif > > namespace std _GLIBCXX_VISIBILITY(default) > > { > > _GLIBCXX_BEGIN_NAMESPACE_VERSION > >@@ -226,6 +230,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > } > > } > > > >+#if __cpp_lib_span_initializer_list >= 202311L > >+#pragma GCC diagnostic push > >+#pragma GCC diagnostic ignored "-Winit-list-lifetime" > >+ constexpr > >+ explicit(extent != dynamic_extent) > >+ span(initializer_list<value_type> __il) > >+ requires (is_const_v<_Type>) > >+ : _M_extent(__il.size()), _M_ptr(__il.begin()) > > These mem-initializers are in the wrong order (we had an existing > constructor with the same problem, but I pushed a fix less than an > hour ago). > > >+ { > >+ if constexpr (extent != dynamic_extent) > >+ { > >+ __glibcxx_assert(__il.size() == extent); > > It's just occurred to me that this check should be done in one place, > not in every constructor. The __detail::__extent_storage<extent> > constructor can check it. > > I'll prepare a patch to do that,
Et voila: https://gcc.gnu.org/pipermail/gcc-patches/2024-December/671432.html > please just remove this if-constexpr > block and the __glibcxx_assert, just saying _M_extent(__il.size()) > will include the assertion soon :-) > > With those two changes, this looks OK to push (disabling the warning > for now is our only option, but I'll continue looking into whether we > can re-enable it and have the front end do the right thing). > > >+ } > >+ } > >+#pragma GCC diagnostic pop > >+#endif > >+ > > constexpr > > span(const span&) noexcept = default; > > > >diff --git a/libstdc++-v3/testsuite/23_containers/span/init_list_cons.cc > >b/libstdc++-v3/testsuite/23_containers/span/init_list_cons.cc > >new file mode 100644 > >index 00000000000..7aabbf1e809 > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/23_containers/span/init_list_cons.cc > >@@ -0,0 +1,65 @@ > >+// { dg-do compile { target c++26 } } > >+ > >+#include <span> > >+#include <type_traits> > >+ > >+#if !defined(__cpp_lib_span_initializer_list) > >+# error "__cpp_lib_span_initializer_list should be defined" > >+#elif __cpp_lib_span_initializer_list < 202311L > >+# error "Wrong value for __cpp_lib_span_initializer_list (should be >= > >202311L)" > >+#endif > >+ > >+// Check the constraint on the initialier_list constructor > >+static_assert( std::is_const_v<std::span<const int>::element_type>); > >+static_assert(!std::is_const_v<std::span< int>::element_type>); > >+ > >+static_assert( std::is_constructible_v<std::span<const int >, > >std::initializer_list< int>>); > >+static_assert( std::is_constructible_v<std::span<const int >, > >std::initializer_list<const int>>); > >+static_assert( std::is_constructible_v<std::span<const int, 42>, > >std::initializer_list< int>>); > >+static_assert( std::is_constructible_v<std::span<const int, 42>, > >std::initializer_list<const int>>); > >+static_assert(!std::is_constructible_v<std::span< int >, > >std::initializer_list< int>>); > >+static_assert(!std::is_constructible_v<std::span< int >, > >std::initializer_list<const int>>); > >+static_assert(!std::is_constructible_v<std::span< int, 42>, > >std::initializer_list< int>>); > >+static_assert(!std::is_constructible_v<std::span< int, 42>, > >std::initializer_list<const int>>); > >+ > >+// Check the explicit-ness on the initialier_list constructor > >+static_assert( std::is_convertible_v<std::initializer_list< int>, > >std::span<const int >>); > >+static_assert( std::is_convertible_v<std::initializer_list<const int>, > >std::span<const int >>); > >+static_assert(!std::is_convertible_v<std::initializer_list< int>, > >std::span<const int, 42>>); > >+static_assert(!std::is_convertible_v<std::initializer_list<const int>, > >std::span<const int, 42>>); > >+static_assert(!std::is_convertible_v<std::initializer_list< int>, > >std::span< int >>); > >+static_assert(!std::is_convertible_v<std::initializer_list<const int>, > >std::span< int >>); > >+static_assert(!std::is_convertible_v<std::initializer_list< int>, > >std::span< int, 42>>); > >+static_assert(!std::is_convertible_v<std::initializer_list<const int>, > >std::span< int, 42>>); > >+ > >+constexpr size_t fun1(std::span<const int> s) > >+{ > >+ return s.size(); > >+} > >+ > >+static_assert(fun1({}) == 0); > >+static_assert(fun1({1, 2, 3}) == 3); > >+static_assert(fun1(std::initializer_list<int>{1, 2, 3}) == 3); > >+ > >+// Stress-test array->pointer decays > >+struct decayer { > >+ constexpr decayer() = default; > >+ constexpr decayer(decayer *) {} > >+}; > >+ > >+constexpr size_t fun2(std::span<const decayer> s) > >+{ > >+ return s.size(); > >+} > >+ > >+void test01() > >+{ > >+ int intArray[42]; > >+ static_assert(fun1(intArray) == 42); > >+ > >+ decayer decArray[42]; > >+ static_assert(fun2(decArray) == 42); > >+ static_assert(fun2({decArray}) == 1); // decayer[] -> decayer* -> > >decayer(decayer*) -> init_list<decayer> of 1 element > >+ static_assert(fun2({decArray, decArray + 42}) == 2); // does not select > >span(iterator, iterator) > >+ static_assert(fun2({decArray, decArray, decArray}) == 3); > >+} > >diff --git a/libstdc++-v3/testsuite/23_containers/span/init_list_cons_neg.cc > >b/libstdc++-v3/testsuite/23_containers/span/init_list_cons_neg.cc > >new file mode 100644 > >index 00000000000..ef43541b769 > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/23_containers/span/init_list_cons_neg.cc > >@@ -0,0 +1,31 @@ > >+// { dg-do run { target c++20 } } > >+ > >+#include <span> > >+#include <utility> > >+#include <any> > >+ > >+#include <testsuite_hooks.h> > >+ > >+// Examples from P2447R4 > >+void one(std::pair<int, int>) {} > >+void one(std::span<const int>) {} > >+void two(std::span<const int, 2>) {} > >+constexpr std::size_t three(std::span<void * const> v) { return v.size(); } > >+constexpr std::size_t four(std::span<const std::any> v) { return v.size(); } > >+ > >+int main() > >+{ > >+ one({1, 2}); // { dg-error "call of overloaded" "should be ambiguous with > >the one(std::pair) overload" { target c++26 } } > >+ two({{1, 2}}); // { dg-error "would use explicit constructor" "should > >prefer the initializer_list constructor, which is explicit" { target c++26 } > >} > >+ > >+ void *array3[10]; > >+ std::any array4[10]; > >+ > >+#if __cpp_lib_span_initializer_list > >+ static_assert(three({array3, 0}) == 2); > >+ VERIFY(four({array4, array4 + 10}) == 2); > >+#else > >+ static_assert(three({array3, 0}) == 0); > >+ VERIFY(four({array4, array4 + 10}) == 10); > >+#endif > >+} > >-- > >2.34.1 > > > > >