https://gcc.gnu.org/g:9df83006384d5d6dbfc2bb53f090791862f7ed67

commit r17-714-g9df83006384d5d6dbfc2bb53f090791862f7ed67
Author: Tomasz Kamiński <[email protected]>
Date:   Mon May 18 13:14:25 2026 +0200

    libstdc++: Add data/member for initializer_list, and remove std overloads
    
    This implements changes section 4.6 of P3016R6, and initializer_list related
    parts of 4.7 and 4.8. The change makes immediately dangling invocations of
    std::begin, std::end, and std::data on braced-init list ill-formed (see
    range_access_neg.cc and range_access17_neg.cc):
      auto it = std::begin({1, 2, 3});  // ILL-FORMED, it was dangling
      (it == std::end({1, 2, 3}));      // ILL-FORMED, was unspecified
      auto* ptr = std::data({1, 2, 3}); // ILL-FORMED, ptr was dangling
    However, similary problemetic calls for std::rbegin, std::rend remain
    well-formed (see range_access14_neg.cc), as initializer_list overloads
    are preserved for these functions:
      auto rit = ranges::rbegin({1, 2, 3}); // COMPILES, dangling
      auto rend = ranges::rend({1, 2, 3});  // COMPILES, danging
    Note, that non-problematic std::size({1, 2, 3}) and std::empty({1, 2, 3})
    use c-array overloads, and remain well-formed.
    
    Per paper, to keep std::data(il) and std::empty(il) well-formed, the data
    and empty member are added to initializer_list.
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/version.def (initializer_list): Define with value
            202511 for C++26.
            * include/bits/version.h: Regenerate.
            * libsupc++/initializer_list (initializer_list::data)
            (initializer_list::empty) [__glibcxx_initializer_list >= 202511L]:
            Define.
            (std::begin(initializer_list<_Tp>), 
std::end(initializer_list<_Tp>)):
            Define only if __glibcxx_initializer_list < 202511L (i.e. not 
defined).
            * include/bits/range_access.h (std::empty(initializer_list<_Tp>))
            (std::data(initializer_list<_Tp>)): Define only if
            __glibcxx_initializer_list < 202511L (i.e. not defined).
            * testsuite/18_support/initializer_list/range_access.cc: Move test 
for
            brace-init list to range_access_neg.c. Included <iterator> in C++26 
or
            later mode.
            * testsuite/18_support/initializer_list/data_empty_mem.cc: New test.
            * testsuite/18_support/initializer_list/range_access14.cc: New test.
            * testsuite/18_support/initializer_list/range_access14_neg.cc: New 
test.
            * testsuite/18_support/initializer_list/range_access17.cc: New test.
            * testsuite/18_support/initializer_list/range_access17_neg.cc: New 
test.
            * testsuite/18_support/initializer_list/range_access_neg.cc: New 
test.
    
    Reviewed-by: Jonathan Wakely <[email protected]>
    Signed-off-by: Tomasz Kamiński <[email protected]>

Diff:
---
 libstdc++-v3/include/bits/range_access.h           |  4 ++++
 libstdc++-v3/include/bits/version.def              |  8 ++++++++
 libstdc++-v3/include/bits/version.h                | 10 +++++++++
 libstdc++-v3/libsupc++/initializer_list            | 13 ++++++++++++
 .../18_support/initializer_list/data_empty_mem.cc  | 21 +++++++++++++++++++
 .../18_support/initializer_list/range_access.cc    | 10 +++------
 .../18_support/initializer_list/range_access14.cc  | 24 ++++++++++++++++++++++
 .../initializer_list/range_access14_neg.cc         | 18 ++++++++++++++++
 .../18_support/initializer_list/range_access17.cc  | 16 +++++++++++++++
 .../initializer_list/range_access17_neg.cc         | 14 +++++++++++++
 .../initializer_list/range_access_neg.cc           | 13 ++++++++++++
 11 files changed, 144 insertions(+), 7 deletions(-)

diff --git a/libstdc++-v3/include/bits/range_access.h 
b/libstdc++-v3/include/bits/range_access.h
index 5a748257f197..b89129f0233b 100644
--- a/libstdc++-v3/include/bits/range_access.h
+++ b/libstdc++-v3/include/bits/range_access.h
@@ -328,6 +328,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     empty(const _Tp (&)[_Nm]) noexcept
     { return false; }
 
+#if __glibcxx_initializer_list < 202511L
   /**
    *  @brief  Return whether an initializer_list is empty.
    *  @param  __il  Initializer list.
@@ -338,6 +339,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     constexpr bool
     empty(initializer_list<_Tp> __il) noexcept
     { return __il.size() == 0;}
+#endif
 
   /**
    *  @brief  Return the data pointer of a container.
@@ -374,6 +376,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     data(_Tp (&__array)[_Nm]) noexcept
     { return __array; }
 
+#if __glibcxx_initializer_list < 202511L
   /**
    *  @brief  Return the data pointer of an initializer list.
    *  @param  __il  Initializer list.
@@ -384,6 +387,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     constexpr const _Tp*
     data(initializer_list<_Tp> __il) noexcept
     { return __il.begin(); }
+#endif
 #endif // __glibcxx_nonmember_container_access
 
 #ifdef __glibcxx_ssize // C++ >= 20
diff --git a/libstdc++-v3/include/bits/version.def 
b/libstdc++-v3/include/bits/version.def
index 1f0d3a2670e5..14337b5b6b6d 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -2410,6 +2410,14 @@ ftms = {
   };
 };
 
+ftms = {
+  name = initializer_list;
+  values = {
+    v = 202511;
+    cxxmin = 26;
+  };
+};
+
 // Standard test specifications.
 stds[97] = ">= 199711L";
 stds[03] = ">= 199711L";
diff --git a/libstdc++-v3/include/bits/version.h 
b/libstdc++-v3/include/bits/version.h
index 66ac0ebef680..9402f25df375 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -2675,4 +2675,14 @@
 #endif /* !defined(__cpp_lib_is_structural) */
 #undef __glibcxx_want_is_structural
 
+#if !defined(__cpp_lib_initializer_list)
+# if (__cplusplus >  202302L)
+#  define __glibcxx_initializer_list 202511L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_initializer_list)
+#   define __cpp_lib_initializer_list 202511L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_initializer_list) */
+#undef __glibcxx_want_initializer_list
+
 #undef __glibcxx_want_all
diff --git a/libstdc++-v3/libsupc++/initializer_list 
b/libstdc++-v3/libsupc++/initializer_list
index baf47baa5f86..fbea49dfb012 100644
--- a/libstdc++-v3/libsupc++/initializer_list
+++ b/libstdc++-v3/libsupc++/initializer_list
@@ -40,6 +40,9 @@
 
 #include <bits/c++config.h>
 
+#define __glibcxx_want_initializer_list
+#include <bits/version.h>
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
   /// initializer_list
@@ -77,8 +80,17 @@ namespace std _GLIBCXX_VISIBILITY(default)
       // One past the last element.
       constexpr const_iterator
       end() const noexcept { return begin() + size(); }
+
+#if __glibcxx_initializer_list >= 202511L
+      constexpr bool
+      empty() const noexcept { return _M_len == 0; }
+
+      constexpr const value_type*
+      data() const noexcept { return _M_array; }
+#endif
     };
 
+#if __glibcxx_initializer_list < 202511L
   /**
    *  @brief  Return an iterator pointing to the first element of
    *          the initializer_list.
@@ -100,6 +112,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
     constexpr const _Tp*
     end(initializer_list<_Tp> __ils) noexcept
     { return __ils.end(); }
+#endif // __glibcxx_initializer_list < 202511L
 }
 
 #endif // C++11
diff --git 
a/libstdc++-v3/testsuite/18_support/initializer_list/data_empty_mem.cc 
b/libstdc++-v3/testsuite/18_support/initializer_list/data_empty_mem.cc
new file mode 100644
index 000000000000..d5673ebf5922
--- /dev/null
+++ b/libstdc++-v3/testsuite/18_support/initializer_list/data_empty_mem.cc
@@ -0,0 +1,21 @@
+// { dg-do compile { target c++26 } }
+
+#include <initializer_list>
+#include <iterator>
+
+#ifndef __cpp_lib_initializer_list
+# error "Feature-test macro for text_encoding missing in <initializer_list>"
+#elif __cpp_lib_initializer_list != 202511L
+# error "Feature-test macro for text_encoding has wrong value in 
<initializer_list>"
+#endif
+
+void
+test02()
+{
+  static constexpr std::initializer_list<int> il{1};
+  static_assert( il.data() == il.begin() );
+  static_assert( il.empty() == false );
+  static_assert( noexcept(il.data()) );
+  static_assert( noexcept(il.empty()) );
+}
+
diff --git a/libstdc++-v3/testsuite/18_support/initializer_list/range_access.cc 
b/libstdc++-v3/testsuite/18_support/initializer_list/range_access.cc
index 8d7cb0dfb3f1..feffd4ee4e43 100644
--- a/libstdc++-v3/testsuite/18_support/initializer_list/range_access.cc
+++ b/libstdc++-v3/testsuite/18_support/initializer_list/range_access.cc
@@ -20,16 +20,12 @@
 // 18.9.3 Initializer list range access [support.initlist.range]
 
 #include <initializer_list>
+#if __cpp_lib_initializer_list >= 202511L
+#  include <iterator>
+#endif
 
 void
 test01()
-{
-  std::begin({1, 2, 3});
-  std::end({1, 2, 3});
-}
-
-void
-test02()
 {
   static constexpr std::initializer_list<int> il{1};
   static_assert( std::begin(il) == il.begin() );
diff --git 
a/libstdc++-v3/testsuite/18_support/initializer_list/range_access14.cc 
b/libstdc++-v3/testsuite/18_support/initializer_list/range_access14.cc
new file mode 100644
index 000000000000..b98e9ccf7b53
--- /dev/null
+++ b/libstdc++-v3/testsuite/18_support/initializer_list/range_access14.cc
@@ -0,0 +1,24 @@
+// { dg-do compile { target c++14 } }
+
+#include <initializer_list>
+#include <iterator>
+
+void
+test01()
+{
+  static constexpr std::initializer_list<int> il{1, 2};
+  static_assert( std::cbegin(il) == il.begin() );
+  static_assert( std::cend(il) == il.end() );
+#if __cplusplus >= 201703L
+  static_assert( std::rbegin(il).base() == il.end() );
+  static_assert( std::rend(il).base() == il.begin() );
+  static_assert( std::rbegin(il).base() == il.end() );
+  static_assert( std::rend(il).base() == il.begin() );
+#endif 
+  static_assert( noexcept(std::cbegin(il)) );
+  static_assert( noexcept(std::cend(il)) );
+  static_assert( noexcept(std::rbegin(il)) );
+  static_assert( noexcept(std::rend(il)) );
+  static_assert( noexcept(std::crbegin(il)) );
+  static_assert( noexcept(std::crend(il)) );
+}
diff --git 
a/libstdc++-v3/testsuite/18_support/initializer_list/range_access14_neg.cc 
b/libstdc++-v3/testsuite/18_support/initializer_list/range_access14_neg.cc
new file mode 100644
index 000000000000..e1c4062fc674
--- /dev/null
+++ b/libstdc++-v3/testsuite/18_support/initializer_list/range_access14_neg.cc
@@ -0,0 +1,18 @@
+// { dg-do compile { target c++17 } }
+
+#include <iterator>
+
+void
+test01()
+{
+  (void)std::cbegin({1, 2, 3});  // { dg-error "no matching function for call" 
}
+  (void)std::cend({1, 2, 3});    // { dg-error "no matching function for call" 
}
+  (void)std::rbegin({1, 2, 3});  // initializer_list overload not removed
+  (void)std::rend({1, 2, 3});    // initializer_list overload not removed
+  (void)std::crbegin({1, 2, 3}); // { dg-error "no matching function for call" 
}
+  (void)std::crend({1, 2, 3});   // { dg-error "no matching function for call" 
}
+
+}
+
+// { dg-prune-output "cannot bind non-const lvalue reference of type" }
+// { dg-prune-output "which is of non-class type" }
diff --git 
a/libstdc++-v3/testsuite/18_support/initializer_list/range_access17.cc 
b/libstdc++-v3/testsuite/18_support/initializer_list/range_access17.cc
new file mode 100644
index 000000000000..443aae3aaa03
--- /dev/null
+++ b/libstdc++-v3/testsuite/18_support/initializer_list/range_access17.cc
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++17 } }
+
+#include <initializer_list>
+#include <iterator>
+
+void
+test01()
+{
+  static constexpr std::initializer_list<int> il{1};
+  static_assert( std::data(il) == il.begin() );
+  static_assert( std::size(il) == il.size() );
+  static_assert( !std::empty(il) );
+  static_assert( noexcept(std::data(il)) );
+  static_assert( noexcept(std::size(il)) );
+  static_assert( noexcept(std::empty(il)) );
+}
diff --git 
a/libstdc++-v3/testsuite/18_support/initializer_list/range_access17_neg.cc 
b/libstdc++-v3/testsuite/18_support/initializer_list/range_access17_neg.cc
new file mode 100644
index 000000000000..fbe18f78bf81
--- /dev/null
+++ b/libstdc++-v3/testsuite/18_support/initializer_list/range_access17_neg.cc
@@ -0,0 +1,14 @@
+// { dg-do compile { target c++17 } }
+
+#include <iterator>
+
+void
+test01()
+{
+  (void)std::data({1, 2, 3});  // { dg-error "no matching function for call" 
"" { target c++26 } }
+  (void)std::size({1, 2, 3});  // uses array overload
+  (void)std::empty({1, 2, 3}); // uses array overload
+}
+
+// { dg-prune-output "cannot bind non-const lvalue reference of type" }
+// { dg-prune-output "which is of non-class type" }
diff --git 
a/libstdc++-v3/testsuite/18_support/initializer_list/range_access_neg.cc 
b/libstdc++-v3/testsuite/18_support/initializer_list/range_access_neg.cc
new file mode 100644
index 000000000000..e4bff4ecbed7
--- /dev/null
+++ b/libstdc++-v3/testsuite/18_support/initializer_list/range_access_neg.cc
@@ -0,0 +1,13 @@
+// { dg-do compile { target c++11 } }
+
+#include <iterator>
+
+void
+test01()
+{
+  (void)std::begin({1, 2, 3}); // { dg-error "no matching function for call" 
"" { target c++26 } }
+  (void)std::end({1, 2, 3});   // { dg-error "no matching function for call" 
"" { target c++26 } }
+}
+
+// { dg-prune-output "cannot bind non-const lvalue reference of type" }
+// { dg-prune-output "which is of non-class type" }

Reply via email to