libstdc++: add initializer_list constructor to std::span (P2447)
Checks
Commit Message
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
Comments
On Tue, 3 Dec 2024 at 16:07, Giuseppe D'Angelo
<giuseppe.dangelo@kdab.com> 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.
Hmm, the warning is useful if somebody does:
std::span s({1,2,3});
So I think we do want the warning, but maybe give it a special case so
it doesn't warn for an rvalue std::span.
Alternatively, we'd want that constructor to be marked with the
opposite of [[nodiscard]], saying to warn if it *isn't* discarded!
On 11/12/2024 21:41, Jonathan Wakely wrote:
> Hmm, the warning is useful if somebody does:
>
> std::span s({1,2,3});
>
> So I think we do want the warning, but maybe give it a special case so
> it doesn't warn for an rvalue std::span.
Do you have anything in mind to make this work?
Note that the above is akin to:
std::vector<int> getVector();
std::span<const int> sp = getVector();
which is also similar to:
std::string getString();
std::string_view sv = getString();
and none of these warn at the moment.
Thank you,
--
Giuseppe D'Angelo
On Wed, 11 Dec 2024 at 21:18, Giuseppe D'Angelo
<giuseppe.dangelo@kdab.com> wrote:
>
> On 11/12/2024 21:41, Jonathan Wakely wrote:
> > Hmm, the warning is useful if somebody does:
> >
> > std::span s({1,2,3});
> >
> > So I think we do want the warning, but maybe give it a special case so
> > it doesn't warn for an rvalue std::span.
>
> Do you have anything in mind to make this work?
It would need changes to the front end, but I think they'd be useful changes.
>
> Note that the above is akin to:
>
> std::vector<int> getVector();
> std::span<const int> sp = getVector();
>
>
> which is also similar to:
>
> std::string getString();
> std::string_view sv = getString();
>
>
> and none of these warn at the moment.
They should do though :-)
If C++ had ref-qualified constructors then we could constrain the
std::span(Range&&)& constructor to only work for borrowed_ranges but
allow the std::span(Range&&)&& constructor to work for any ranges. We
don't have that though.
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.dangelo@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, 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
>
On Wed, 11 Dec 2024 at 22:53, Jonathan Wakely <jwakely@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.dangelo@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
> >
>
>
>
Hi,
On 12/12/2024 01:04, Jonathan Wakely wrote:
>> I'll prepare a patch to do that,
> Et voila:
> https://gcc.gnu.org/pipermail/gcc-patches/2024-December/671432.html
Thanks! All done, new patch is attached.
>
> 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).
Which of course I c&p from. Are there some tests meant to be ran under
-Wreorder?
Cheers,
--
Giuseppe D'Angelo
On Thu, 12 Dec 2024 at 14:24, Giuseppe D'Angelo
<giuseppe.dangelo@kdab.com> wrote:
>
> Hi,
>
> On 12/12/2024 01:04, Jonathan Wakely wrote:
> >> I'll prepare a patch to do that,
> > Et voila:
> > https://gcc.gnu.org/pipermail/gcc-patches/2024-December/671432.html
>
> Thanks! All done, new patch is attached.
Thanks. Do you have any thoughts on my patch for the std::span assertions?
> >
> > 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).
>
> Which of course I c&p from. Are there some tests meant to be ran under
> -Wreorder?
We should be using -Wall for the whole testsuite ... but we're not,
which is strange. I'll have to look into that.
On Thu, 12 Dec 2024 at 14:24, Giuseppe D'Angelo
<giuseppe.dangelo@kdab.com> wrote:
>
> Hi,
>
> On 12/12/2024 01:04, Jonathan Wakely wrote:
> >> I'll prepare a patch to do that,
> > Et voila:
> > https://gcc.gnu.org/pipermail/gcc-patches/2024-December/671432.html
>
> Thanks! All done, new patch is attached.
I was about to push this and realised it's missing a Signed-off-by
tag. I assume you meant to contribute this under the DCO terms, as
with your previous patches?
Hello,
On 19/12/2024 13:27, Jonathan Wakely wrote:
> I was about to push this and realised it's missing a Signed-off-by
> tag. I assume you meant to contribute this under the DCO terms, as
> with your previous patches?
Yes, of course; sorry for forgetting the line. Here's a signed patch.
--
Giuseppe D'Angelo
On Thu, 19 Dec 2024 at 13:49, Giuseppe D'Angelo
<giuseppe.dangelo@kdab.com> wrote:
>
> Hello,
>
> On 19/12/2024 13:27, Jonathan Wakely wrote:
> > I was about to push this and realised it's missing a Signed-off-by
> > tag. I assume you meant to contribute this under the DCO terms, as
> > with your previous patches?
>
> Yes, of course; sorry for forgetting the line. Here's a signed patch.
Great, thanks for the prompt reply! I'll test and get it pushed.
On 19/12/24 14:49 +0100, Giuseppe D'Angelo wrote:
>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);
This runtime VERIFY assertion doesn't make sense because the file
doesn't compile at all in C++26 mode, it fails due to the errors
above.
>+#else
>+ static_assert(three({array3, 0}) == 0);
>+ VERIFY(four({array4, array4 + 10}) == 10);
>+#endif
>+}
>--
>2.34.1
>
I'm seeing failures for this new test when compiled as C++26, e.g.
when using
GLIBCXX_TESTSUITE_STDS=20,23,26 make check RUNTESTFLAGS="conformance.exp=23_containers/span/init_list_cons_neg.cc -a"
The results are:
PASS: 23_containers/span/init_list_cons_neg.cc -std=gnu++20 (test for excess errors)
PASS: 23_containers/span/init_list_cons_neg.cc -std=gnu++20 execution test
PASS: 23_containers/span/init_list_cons_neg.cc -std=gnu++23 (test for excess errors)
PASS: 23_containers/span/init_list_cons_neg.cc -std=gnu++23 execution test
PASS: 23_containers/span/init_list_cons_neg.cc -std=gnu++26 should be ambiguous with the one(std::pair) overload (test for errors, line 18)
PASS: 23_containers/span/init_list_cons_neg.cc -std=gnu++26 should prefer the initializer_list constructor, which is explicit (test for errors, line 19)
PASS: 23_containers/span/init_list_cons_neg.cc -std=gnu++26 (test for excess errors)
UNRESOLVED: 23_containers/span/init_list_cons_neg.cc -std=gnu++26 compilation failed to produce executable
The problem is that { dg-do run ... } means that dejagnu expects the
file to compile and run, but it fails to compile for C++26 so cannot
be run.
I think what's needed is:
// { dg-do run { target { c++20 && c++23_down } } }
// { dg-do compile { target c++26 } }
This makes it a compile+link+run test for C++20/23 but only a compile
test for C++26, so it won't complain about it being UNRESOLVED for the
run step.
To solve the problem of the VERIFY not being checked for C++26, let's
repalce std::any with a constexpr-compatible Any type and move the
arrays to namespace scope, like so:
// { dg-do run { target { c++20 && c++23_down } } }
// { dg-do compile { target c++26 } }
#include <span>
#include <utility>
#include <testsuite_hooks.h>
struct Any {
constexpr Any() { }
template<typename T> constexpr Any(T) { }
};
// 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 Any> v) { return v.size(); }
void *array3[10];
Any array4[10];
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 } }
#if __cpp_lib_span_initializer_list
static_assert(three({array3, 0}) == 2);
static_assert(four({array4, array4 + 10}) == 2);
#else
static_assert(three({array3, 0}) == 0);
static_assert(four({array4, array4 + 10}) == 10);
#endif
}
With this, I get seven PASS results for this test.
Hi,
On 20/12/2024 13:37, Jonathan Wakely wrote:
>
> With this, I get seven PASS results for this test.
Thank you; I was just thinking, would it be simpler to split the test in
two instead rather than making it more convoluted than necessary? Let me
know and I'll amend it, in either direction.
--
Giuseppe D'Angelo
On Fri, 20 Dec 2024 at 14:21, Giuseppe D'Angelo
<giuseppe.dangelo@kdab.com> wrote:
>
> Hi,
>
> On 20/12/2024 13:37, Jonathan Wakely wrote:
> >
> > With this, I get seven PASS results for this test.
>
> Thank you; I was just thinking, would it be simpler to split the test in
> two instead rather than making it more convoluted than necessary? Let me
> know and I'll amend it, in either direction.
I did think about that, but I think it's OK in a single test. That
will also run a tiny bit quicker, as we won't have two tests which are
skipped for most targets, we'll just have one which is only skipped
for C++17 down.
And I don't expect this test to need to change in future, so it
shouldn't get any more convoluted than it is now, and won't be a
maintenance headache.
So I think I'll just finish testing it like that, as one test, and push that.
Hello,
On 20/12/2024 18:05, Jonathan Wakely wrote:
> So I think I'll just finish testing it like that, as one test, and push that.
OK, thanks.
But in fact, I've realized that it doesn't need to be run at all?
There's nothing that happens at runtime. I've therefore made the test
just a compile time one.
Thanks,
--
Giuseppe D'Angelo
From bb1d537ee7b68883403127903834427c6787c505 Mon Sep 17 00:00:00 2001
From: Giuseppe D'Angelo <giuseppe.dangelo@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
@@ -1853,6 +1853,14 @@ ftms = {
};
};
+ftms = {
+ name = span_initializer_list;
+ values = {
+ v = 202311;
+ cxxmin = 26;
+ };
+};
+
ftms = {
name = text_encoding;
values = {
@@ -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
@@ -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;
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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