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