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())
+	{
+	  if constexpr (extent != dynamic_extent)
+	    {
+	      __glibcxx_assert(__il.size() == extent);
+	    }
+	}
+#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

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

Reply via email to