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

Reply via email to