From 6159bfcf8b5852417519cf4181bf0ff8907ac0ca Mon Sep 17 00:00:00 2001 From: Matthias Wippich Date: Thu, 9 Apr 2026 04:41:31 +0200 Subject: [PATCH] libstdc++: Implement structured binding support for integer_sequence This implements P1789R3 Library Support for Expansion Statements. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p1789r3.pdf libstdc++-v3/ChangeLog: * include/bits/utility.h (std::tuple_size, std::tuple_element): Add partial specializations for integer_sequence. (std::get(integer_sequence<_Tp, _Idx...>)): Define. * include/bits/version.def (integer_sequence): Bump to 202511L. * include/bits/version.h: Regenerate. * testsuite/20_util/integer_sequence/tuple_access.cc: New test. * testsuite/20_util/integer_sequence/tuple_access_neg.cc: New test. * testsuite/experimental/feat-cxx14.cc: Updated check for __cpp_lib_integer_sequence value. Reviewed-by: Jonathan Wakely Co-authored-by: Ivan Lazaric Signed-off-by: Matthias Wippich --- libstdc++-v3/include/bits/utility.h | 36 +++++ libstdc++-v3/include/bits/version.def | 5 + libstdc++-v3/include/bits/version.h | 7 +- .../20_util/integer_sequence/tuple_access.cc | 143 ++++++++++++++++++ .../integer_sequence/tuple_access_neg.cc | 22 +++ .../testsuite/experimental/feat-cxx14.cc | 4 +- 6 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/integer_sequence/tuple_access.cc create mode 100644 libstdc++-v3/testsuite/20_util/integer_sequence/tuple_access_neg.cc diff --git a/libstdc++-v3/include/bits/utility.h b/libstdc++-v3/include/bits/utility.h index 93e9e9f9dba..74ae04d6bd2 100644 --- a/libstdc++-v3/include/bits/utility.h +++ b/libstdc++-v3/include/bits/utility.h @@ -481,6 +481,42 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static constexpr size_t size() noexcept { return sizeof...(_Idx); } }; +#if __glibcxx_integer_sequence >= 202511L // C++ >= 26 + /** @brief Structured binding support for `integer_sequence` + + * @since C++26 + * @{ + */ + /// Structured binding support + template + struct tuple_size> + : integral_constant { }; + + template + struct tuple_element<__i, integer_sequence<_Tp, _Idx...>> + { + static_assert(__i < sizeof...(_Idx)); + using type = _Tp; + }; + + template + struct tuple_element<__i, const integer_sequence<_Tp, _Idx...>> + { + static_assert(__i < sizeof...(_Idx)); + using type = _Tp; + }; + + template + [[nodiscard]] + constexpr _Tp + get(integer_sequence<_Tp, _Idx...>) noexcept + { + static_assert(__i < sizeof...(_Idx)); + return _Idx...[__i]; + } + /// @} +#endif // __glibcxx_integer_sequence >= 202511L + /// Alias template make_integer_sequence template using make_integer_sequence diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index b88d9c3483f..7aaf150d087 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -184,6 +184,11 @@ ftms = { ftms = { name = integer_sequence; + values = { + v = 202511; + cxxmin = 26; + extra_cond = "__cpp_pack_indexing"; + }; values = { v = 201304; cxxmin = 14; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 24ec0175e1e..b247662f089 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -186,7 +186,12 @@ #undef __glibcxx_want_exchange_function #if !defined(__cpp_lib_integer_sequence) -# if (__cplusplus >= 201402L) +# if (__cplusplus > 202302L) && (__cpp_pack_indexing) +# define __glibcxx_integer_sequence 202511L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_integer_sequence) +# define __cpp_lib_integer_sequence 202511L +# endif +# elif (__cplusplus >= 201402L) # define __glibcxx_integer_sequence 201304L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_integer_sequence) # define __cpp_lib_integer_sequence 201304L diff --git a/libstdc++-v3/testsuite/20_util/integer_sequence/tuple_access.cc b/libstdc++-v3/testsuite/20_util/integer_sequence/tuple_access.cc new file mode 100644 index 00000000000..afa644e7dab --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/integer_sequence/tuple_access.cc @@ -0,0 +1,143 @@ +// { dg-do compile { target c++26 } } + +#include +#include +#include + +#if __cpp_lib_integer_sequence < 202511L +# error "Feature-test macro __cpp_lib_integer_sequence is incorrect" +#endif + +constexpr auto +destructure_sum(auto seq) +{ + auto [...elems] = seq; + return (0 + ... + elems); +} + +using IS1 = std::make_index_sequence<10>; +static_assert( std::tuple_size_v == 10 ); +static_assert( std::is_same_v, std::size_t> ); +static_assert( std::get<7>(IS1{}) == 7 ); +static_assert( destructure_sum(IS1{}) == 45 ); +static_assert( noexcept(get<0>(IS1{})) ); + +using IS2 = std::integer_sequence; +static_assert( std::tuple_size_v == 3 ); +static_assert( std::is_same_v, int> ); +static_assert( std::get<2>(IS2{}) == -13 ); +static_assert( destructure_sum(IS2{}) == 130 ); +static_assert( noexcept(get<0>(IS2{})) ); + +using IS3 = std::integer_sequence; +static_assert( std::tuple_size_v == 0 ); + +using IS4 = std::integer_sequence; +static_assert( !std::is_constructible_v, IS4> ); +static_assert( !std::is_constructible_v, IS4> ); + +template +constexpr bool +test_basic() +{ + { + auto [...elems] = std::make_index_sequence<0>{}; + static_assert( sizeof...(elems) == 0 ); + } + + { + auto [...elems] = std::integer_sequence{}; + + static_assert( sizeof...(elems) == 4 ); + + VERIFY( elems...[0] == 3 ); + VERIFY( elems...[1] == 5 ); + VERIFY( elems...[2] == 7 ); + VERIFY( elems...[3] == 11 ); + } + + { + static constexpr auto [...elems] = std::integer_sequence{}; + + static_assert( sizeof...(elems) == 4 ); + + static_assert( elems...[0] == 2 ); + static_assert( elems...[1] == 4 ); + static_assert( elems...[2] == 8 ); + static_assert( elems...[3] == 16 ); + } + + { + constexpr auto [...elems] = std::integer_sequence{}; + + static_assert( sizeof...(elems) == 4 ); + + static_assert( elems...[0] == 1 ); + static_assert( elems...[1] == 8 ); + static_assert( elems...[2] == 2 ); + static_assert( elems...[3] == 11 ); + } + + { + static constexpr auto&& [...elems] = std::integer_sequence{}; + + static_assert( sizeof...(elems) == 4 ); + + static_assert( elems...[0] == 2 ); + static_assert( elems...[1] == 4 ); + static_assert( elems...[2] == 8 ); + static_assert( elems...[3] == 16 ); + } + + /* Unimplemented, see PR117784 + { + constexpr auto&& [...elems] = std::integer_sequence{}; + + static_assert( sizeof...(elems) == 4 ); + + static_assert( elems...[0] == 1 ); + static_assert( elems...[1] == 8 ); + static_assert( elems...[2] == 2 ); + static_assert( elems...[3] == 11 ); + } + */ + + { + auto idx = 0; + static constexpr auto seq = std::make_index_sequence<4>{}; + template for (constexpr auto elem : seq) + { + VERIFY( elem == idx ); + ++idx; + } + VERIFY( idx == 4 ); + } + + + /* Unimplemented, see PR117784 + { + auto idx = 0; + constexpr auto seq = std::make_index_sequence<4>{}; + template for (constexpr auto elem : seq) + { + VERIFY( elem == idx ); + ++idx; + } + VERIFY( idx == 4 ); + } + + { + auto idx = 0; + template for (constexpr auto elem : std::make_index_sequence<4>{}) + { + VERIFY( elem == idx ); + ++idx; + } + VERIFY( idx == 4 ); + } + */ + + return true; +} + +static_assert( test_basic() ); diff --git a/libstdc++-v3/testsuite/20_util/integer_sequence/tuple_access_neg.cc b/libstdc++-v3/testsuite/20_util/integer_sequence/tuple_access_neg.cc new file mode 100644 index 00000000000..8640114e7d3 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/integer_sequence/tuple_access_neg.cc @@ -0,0 +1,22 @@ +// { dg-do compile { target c++26 } } + +#include + +using empty = std::integer_sequence; +static_assert( std::is_same_v, int> ); // { dg-error "here" } +static_assert( std::is_same_v, int> ); // { dg-error "here" } + +using size4 = std::integer_sequence; +static_assert( std::is_same_v, int> ); // { dg-error "here" } +static_assert( std::is_same_v, int> ); // { dg-error "here" } + +void +test() +{ + (void)std::get<0>(empty{}); // { dg-error "here" } + (void)std::get<4>(size4{}); // { dg-error "here" } +} + +// { dg-error "static assertion failed" "" { target *-*-* } 0 } +// { dg-error "cannot index an empty pack" "" { target *-*-* } 0 } +// { dg-error "pack index '.' is out of range for pack of length" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/experimental/feat-cxx14.cc b/libstdc++-v3/testsuite/experimental/feat-cxx14.cc index c009062b55c..4cc56c5f76e 100644 --- a/libstdc++-v3/testsuite/experimental/feat-cxx14.cc +++ b/libstdc++-v3/testsuite/experimental/feat-cxx14.cc @@ -18,8 +18,8 @@ #ifndef __cpp_lib_integer_sequence # error "__cpp_lib_integer_sequence" -#elif __cpp_lib_integer_sequence != 201304 -# error "__cpp_lib_integer_sequence != 201304" +#elif __cpp_lib_integer_sequence < 201304 +# error "__cpp_lib_integer_sequence < 201304" #endif #ifndef __cpp_lib_exchange_function