From 032858a20aa33fda5370315e7eed7b946c1543be Mon Sep 17 00:00:00 2001 From: Tsche Date: Sat, 8 Nov 2025 23:10:28 +0100 Subject: [PATCH 01/13] [libc++] P1789R3: Library Support for Expansion Statements --- libcxx/docs/FeatureTestMacroTable.rst | 2 + libcxx/docs/ReleaseNotes/22.rst | 1 + libcxx/docs/Status/Cxx2cPapers.csv | 2 + libcxx/include/__utility/integer_sequence.h | 29 +++++++++++ libcxx/include/version | 4 +- .../utility.version.compile.pass.cpp | 4 +- .../version.version.compile.pass.cpp | 4 +- .../intseq.binding/integer_seq.pass.cpp | 49 +++++++++++++++++++ .../intseq.binding/integer_seq.verify.cpp | 24 +++++++++ .../generate_feature_test_macro_components.py | 5 +- 10 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp create mode 100644 libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index d5ed9188b1b23..349164a96de87 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -474,6 +474,8 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_inplace_vector`` *unimplemented* ---------------------------------------------------------- ----------------- + ``__cpp_lib_integer_sequence`` ``202511L`` + ---------------------------------------------------------- ----------------- ``__cpp_lib_is_sufficiently_aligned`` ``202411L`` ---------------------------------------------------------- ----------------- ``__cpp_lib_is_virtual_base_of`` ``202406L`` diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index a6a0ac8670fb5..685e7a22be28a 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -48,6 +48,7 @@ Implemented Papers - P2835R7: Expose ``std::atomic_ref``'s object address (`Github `__) - P2944R3: Comparisons for ``reference_wrapper`` (`Github `__) - P3168R2: Give ``std::optional`` Range Support (`Github `__) +- P1789R3: Library Support for Expansion Statements Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index e0e47b864d38f..7347408fbb3c1 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -157,3 +157,5 @@ "`P3552R3 `__","Add a Coroutine Task Type","2025-06 (Sofia)","","","`#148182 `__","" "`P1317R2 `__","Remove return type deduction in ``std::apply``","2025-06 (Sofia)","","","`#148183 `__","" "","","","","","","" +"`P1789R3 `__","Library Support for Expansion Statements`","2025-11 (Kona)","|Complete|","22","","" +"","","","","","","" diff --git a/libcxx/include/__utility/integer_sequence.h b/libcxx/include/__utility/integer_sequence.h index 329826ae5eda2..8889c1196a450 100644 --- a/libcxx/include/__utility/integer_sequence.h +++ b/libcxx/include/__utility/integer_sequence.h @@ -13,6 +13,11 @@ #include <__cstddef/size_t.h> #include <__type_traits/is_integral.h> +#if _LIBCPP_STD_VER >= 26 +# include <__tuple/tuple_element.h> +# include <__tuple/tuple_size.h> +#endif + #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header #endif @@ -67,6 +72,30 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __for_each_index_sequence(index_sequence<_I } # endif // _LIBCPP_STD_VER >= 20 +# if _LIBCPP_STD_VER >= 26 +// structured binding support for integer_sequence +template +struct tuple_size> : public integral_constant {}; + +template +struct tuple_element<_Ip, integer_sequence<_Tp, _Indices...>> { + static_assert(_Ip < sizeof...(_Indices), "Index out of bounds in std::tuple_element<> (std::integer_sequence)"); + using type _LIBCPP_NODEBUG = _Tp; +}; + +template +struct tuple_element<_Ip, const integer_sequence<_Tp, _Indices...>> { + static_assert(_Ip < sizeof...(_Indices), "Index out of bounds in std::tuple_element<> (const std::integer_sequence)"); + using type _LIBCPP_NODEBUG = _Tp; +}; + +template +_LIBCPP_HIDE_FROM_ABI constexpr _Tp get(integer_sequence<_Tp, _Indices...>) _NOEXCEPT { + static_assert(_Ip < sizeof...(_Indices), "Index out of bounds in std::get<> (std::integer_sequence)"); + return _Indices...[_Ip]; +} +# endif // _LIBCPP_STD_VER >= 20 + # endif // _LIBCPP_STD_VER >= 14 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/version b/libcxx/include/version index b0030602f854a..c9a4c58a9a289 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -141,7 +141,7 @@ __cpp_lib_incomplete_container_elements 201505L < __cpp_lib_inplace_vector 202406L __cpp_lib_int_pow2 202002L __cpp_lib_integer_comparison_functions 202002L -__cpp_lib_integer_sequence 201304L +__cpp_lib_integer_sequence 202511L __cpp_lib_integral_constant_callable 201304L __cpp_lib_interpolate 201902L __cpp_lib_invoke 201411L @@ -582,6 +582,8 @@ __cpp_lib_void_t 201411L // # define __cpp_lib_generate_random 202403L // # define __cpp_lib_hazard_pointer 202306L // # define __cpp_lib_inplace_vector 202406L +# undef __cpp_lib_integer_sequence +# define __cpp_lib_integer_sequence 202511L # define __cpp_lib_is_sufficiently_aligned 202411L # if __has_builtin(__builtin_is_virtual_base_of) # define __cpp_lib_is_virtual_base_of 202406L diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp index b882a5df04ae3..1d82ef9ec0e86 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp @@ -432,8 +432,8 @@ # ifndef __cpp_lib_integer_sequence # error "__cpp_lib_integer_sequence should be defined in c++26" # endif -# if __cpp_lib_integer_sequence != 201304L -# error "__cpp_lib_integer_sequence should have the value 201304L in c++26" +# if __cpp_lib_integer_sequence != 202511L +# error "__cpp_lib_integer_sequence should have the value 202511L in c++26" # endif # if !defined(_LIBCPP_VERSION) diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp index 8189c5c4e5985..355ca57bac64c 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -7156,8 +7156,8 @@ # ifndef __cpp_lib_integer_sequence # error "__cpp_lib_integer_sequence should be defined in c++26" # endif -# if __cpp_lib_integer_sequence != 201304L -# error "__cpp_lib_integer_sequence should have the value 201304L in c++26" +# if __cpp_lib_integer_sequence != 202511L +# error "__cpp_lib_integer_sequence should have the value 202511L in c++26" # endif # ifndef __cpp_lib_integral_constant_callable diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp new file mode 100644 index 0000000000000..8deda77419e66 --- /dev/null +++ b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 +// + +// structured binding support for integer_sequence + +#include +#include +#include +#include + +#include "test_macros.h" + +int main(int, char**) +{ + using empty = std::integer_sequence; + using size4 = std::integer_sequence; + + static_assert ( std::tuple_size_v == 0, "empty size wrong" ); + static_assert ( std::tuple_size_v == 0, "empty size wrong" ); + + static_assert ( std::tuple_size_v == 4, "size4 size wrong" ); + static_assert ( std::tuple_size_v == 4, "size4 size wrong" ); + + static_assert ( std::is_same_v, int>, "size4 type wrong" ); + static_assert ( std::is_same_v, int>, "size4 type wrong" ); + static_assert ( std::is_same_v, int>, "size4 type wrong" ); + static_assert ( std::is_same_v, int>, "size4 type wrong" ); + + static_assert ( std::is_same_v, int>, "const4 type wrong" ); + static_assert ( std::is_same_v, int>, "const4 type wrong" ); + static_assert ( std::is_same_v, int>, "const4 type wrong" ); + static_assert ( std::is_same_v, int>, "const4 type wrong" ); + + constexpr static size4 seq4{}; + static_assert ( get<0> (seq4) == 9, "size4 element 0 wrong" ); + static_assert ( get<1> (seq4) == 8, "size4 element 1 wrong" ); + static_assert ( get<2> (seq4) == 7, "size4 element 2 wrong" ); + static_assert ( get<3> (seq4) == 2, "size4 element 3 wrong" ); + + return 0; +} diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp new file mode 100644 index 0000000000000..9c93d11b28ceb --- /dev/null +++ b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 +// + +// Expect failures for tuple_element and get with empty integer_sequence + +#include + +void f() { + using test1 = typename std::tuple_element<0, std::integer_sequence>::type; // expected-error-re@*:* {{static assertion failed{{.*}}Index out of bounds in std::tuple_element<> (std::integer_sequence)}} + using test2 = typename std::tuple_element<0, const std::integer_sequence>::type; // expected-error-re@*:* {{static assertion failed{{.*}}Index out of bounds in std::tuple_element<> (const std::integer_sequence)}} + + auto empty = std::integer_sequence(); + // expected-error-re@*:* {{static assertion failed{{.*}}Index out of bounds in std::get<> (std::integer_sequence)}} + // expected-error-re@*:* {{invalid index 0 for pack '{{.*}}' of size 0}} + (void)std::get<0>(empty); +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index 22209f53d50d7..914172410e332 100644 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -764,7 +764,10 @@ def add_version_header(tc): }, { "name": "__cpp_lib_integer_sequence", - "values": {"c++14": 201304}, + "values": { + "c++14": 201304, + "c++26": 202511, # P1789R3 Library Support for Expansion Statements + }, "headers": ["utility"], }, { From 74023193c4fb9560f991d25c5ac19f6a86d471be Mon Sep 17 00:00:00 2001 From: Tsche Date: Sat, 8 Nov 2025 23:31:03 +0100 Subject: [PATCH 02/13] fix formatting issues --- .../intseq.binding/integer_seq.pass.cpp | 51 +++++++++---------- .../intseq.binding/integer_seq.verify.cpp | 14 ++--- .../generate_feature_test_macro_components.py | 2 +- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp index 8deda77419e66..7220bccb601a8 100644 --- a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp +++ b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp @@ -18,32 +18,31 @@ #include "test_macros.h" -int main(int, char**) -{ - using empty = std::integer_sequence; - using size4 = std::integer_sequence; - - static_assert ( std::tuple_size_v == 0, "empty size wrong" ); - static_assert ( std::tuple_size_v == 0, "empty size wrong" ); - - static_assert ( std::tuple_size_v == 4, "size4 size wrong" ); - static_assert ( std::tuple_size_v == 4, "size4 size wrong" ); - - static_assert ( std::is_same_v, int>, "size4 type wrong" ); - static_assert ( std::is_same_v, int>, "size4 type wrong" ); - static_assert ( std::is_same_v, int>, "size4 type wrong" ); - static_assert ( std::is_same_v, int>, "size4 type wrong" ); - - static_assert ( std::is_same_v, int>, "const4 type wrong" ); - static_assert ( std::is_same_v, int>, "const4 type wrong" ); - static_assert ( std::is_same_v, int>, "const4 type wrong" ); - static_assert ( std::is_same_v, int>, "const4 type wrong" ); - - constexpr static size4 seq4{}; - static_assert ( get<0> (seq4) == 9, "size4 element 0 wrong" ); - static_assert ( get<1> (seq4) == 8, "size4 element 1 wrong" ); - static_assert ( get<2> (seq4) == 7, "size4 element 2 wrong" ); - static_assert ( get<3> (seq4) == 2, "size4 element 3 wrong" ); +int main(int, char**) { + using empty = std::integer_sequence; + using size4 = std::integer_sequence; + + static_assert(std::tuple_size_v == 0, "empty size wrong"); + static_assert(std::tuple_size_v == 0, "empty size wrong"); + + static_assert(std::tuple_size_v == 4, "size4 size wrong"); + static_assert(std::tuple_size_v == 4, "size4 size wrong"); + + static_assert(std::is_same_v, int>, "size4 type wrong"); + static_assert(std::is_same_v, int>, "size4 type wrong"); + static_assert(std::is_same_v, int>, "size4 type wrong"); + static_assert(std::is_same_v, int>, "size4 type wrong"); + + static_assert(std::is_same_v, int>, "const4 type wrong"); + static_assert(std::is_same_v, int>, "const4 type wrong"); + static_assert(std::is_same_v, int>, "const4 type wrong"); + static_assert(std::is_same_v, int>, "const4 type wrong"); + + constexpr static size4 seq4{}; + static_assert(get<0>(seq4) == 9, "size4 element 0 wrong"); + static_assert(get<1>(seq4) == 8, "size4 element 1 wrong"); + static_assert(get<2>(seq4) == 7, "size4 element 2 wrong"); + static_assert(get<3>(seq4) == 2, "size4 element 3 wrong"); return 0; } diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp index 9c93d11b28ceb..5863a692ad5a3 100644 --- a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp +++ b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp @@ -14,11 +14,13 @@ #include void f() { - using test1 = typename std::tuple_element<0, std::integer_sequence>::type; // expected-error-re@*:* {{static assertion failed{{.*}}Index out of bounds in std::tuple_element<> (std::integer_sequence)}} - using test2 = typename std::tuple_element<0, const std::integer_sequence>::type; // expected-error-re@*:* {{static assertion failed{{.*}}Index out of bounds in std::tuple_element<> (const std::integer_sequence)}} + using test1 = typename std::tuple_element<0, std::integer_sequence>:: + type; // expected-error-re@*:* {{static assertion failed{{.*}}Index out of bounds in std::tuple_element<> (std::integer_sequence)}} + using test2 = typename std::tuple_element<0, const std::integer_sequence>:: + type; // expected-error-re@*:* {{static assertion failed{{.*}}Index out of bounds in std::tuple_element<> (const std::integer_sequence)}} - auto empty = std::integer_sequence(); - // expected-error-re@*:* {{static assertion failed{{.*}}Index out of bounds in std::get<> (std::integer_sequence)}} - // expected-error-re@*:* {{invalid index 0 for pack '{{.*}}' of size 0}} - (void)std::get<0>(empty); + auto empty = std::integer_sequence(); + // expected-error-re@*:* {{static assertion failed{{.*}}Index out of bounds in std::get<> (std::integer_sequence)}} + // expected-error-re@*:* {{invalid index 0 for pack '{{.*}}' of size 0}} + (void)std::get<0>(empty); } diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index 914172410e332..7b29cee34f765 100644 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -766,7 +766,7 @@ def add_version_header(tc): "name": "__cpp_lib_integer_sequence", "values": { "c++14": 201304, - "c++26": 202511, # P1789R3 Library Support for Expansion Statements + "c++26": 202511, # P1789R3 Library Support for Expansion Statements }, "headers": ["utility"], }, From 350a3a1cda9d23a5b1465503a94024e529fed48d Mon Sep 17 00:00:00 2001 From: Tsche Date: Sat, 8 Nov 2025 23:37:19 +0100 Subject: [PATCH 03/13] regenerate --- libcxx/include/version | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libcxx/include/version b/libcxx/include/version index c9a4c58a9a289..3a2d38623a36f 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -142,6 +142,7 @@ __cpp_lib_inplace_vector 202406L __cpp_lib_int_pow2 202002L __cpp_lib_integer_comparison_functions 202002L __cpp_lib_integer_sequence 202511L + 201304L // C++14 __cpp_lib_integral_constant_callable 201304L __cpp_lib_interpolate 201902L __cpp_lib_invoke 201411L @@ -582,7 +583,7 @@ __cpp_lib_void_t 201411L // # define __cpp_lib_generate_random 202403L // # define __cpp_lib_hazard_pointer 202306L // # define __cpp_lib_inplace_vector 202406L -# undef __cpp_lib_integer_sequence +# undef __cpp_lib_integer_sequence # define __cpp_lib_integer_sequence 202511L # define __cpp_lib_is_sufficiently_aligned 202411L # if __has_builtin(__builtin_is_virtual_base_of) From fd296c5fc984fb3c12d02083a2fc57e354a131d5 Mon Sep 17 00:00:00 2001 From: Tsche Date: Sat, 8 Nov 2025 23:58:48 +0100 Subject: [PATCH 04/13] fix failing test --- .../std/utilities/intseq/intseq.binding/integer_seq.verify.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp index 5863a692ad5a3..e25d95fbda32e 100644 --- a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp +++ b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp @@ -21,6 +21,6 @@ void f() { auto empty = std::integer_sequence(); // expected-error-re@*:* {{static assertion failed{{.*}}Index out of bounds in std::get<> (std::integer_sequence)}} - // expected-error-re@*:* {{invalid index 0 for pack '{{.*}}' of size 0}} + // expected-error-re@*:* {{invalid index 0 for pack {{.*}} of size 0}} (void)std::get<0>(empty); } From e309910569bf663630a8e081736a17c54bb31d36 Mon Sep 17 00:00:00 2001 From: Tsche Date: Sun, 9 Nov 2025 06:45:16 +0100 Subject: [PATCH 05/13] apply review comments --- libcxx/docs/Status/Cxx2cPapers.csv | 2 +- libcxx/include/__utility/integer_sequence.h | 4 +-- .../intseq.binding/integer_seq.pass.cpp | 35 +++++++++---------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index 7347408fbb3c1..81604230d651f 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -157,5 +157,5 @@ "`P3552R3 `__","Add a Coroutine Task Type","2025-06 (Sofia)","","","`#148182 `__","" "`P1317R2 `__","Remove return type deduction in ``std::apply``","2025-06 (Sofia)","","","`#148183 `__","" "","","","","","","" -"`P1789R3 `__","Library Support for Expansion Statements`","2025-11 (Kona)","|Complete|","22","","" +"`P1789R3 `__","Library Support for Expansion Statements","2025-11 (Kona)","|Complete|","22","","" "","","","","","","" diff --git a/libcxx/include/__utility/integer_sequence.h b/libcxx/include/__utility/integer_sequence.h index 8889c1196a450..a950431f1b480 100644 --- a/libcxx/include/__utility/integer_sequence.h +++ b/libcxx/include/__utility/integer_sequence.h @@ -75,7 +75,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __for_each_index_sequence(index_sequence<_I # if _LIBCPP_STD_VER >= 26 // structured binding support for integer_sequence template -struct tuple_size> : public integral_constant {}; +struct tuple_size> : integral_constant {}; template struct tuple_element<_Ip, integer_sequence<_Tp, _Indices...>> { @@ -94,7 +94,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp get(integer_sequence<_Tp, _Indices...>) _NOE static_assert(_Ip < sizeof...(_Indices), "Index out of bounds in std::get<> (std::integer_sequence)"); return _Indices...[_Ip]; } -# endif // _LIBCPP_STD_VER >= 20 +# endif // _LIBCPP_STD_VER >= 26 # endif // _LIBCPP_STD_VER >= 14 diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp index 7220bccb601a8..fc3f039c1d265 100644 --- a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp +++ b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp @@ -20,29 +20,28 @@ int main(int, char**) { using empty = std::integer_sequence; + static_assert(std::tuple_size_v == 0); + static_assert(std::tuple_size_v == 0); + using size4 = std::integer_sequence; + static_assert(std::tuple_size_v == 4); + static_assert(std::tuple_size_v == 4); - static_assert(std::tuple_size_v == 0, "empty size wrong"); - static_assert(std::tuple_size_v == 0, "empty size wrong"); + static_assert(std::is_same_v, int>); + static_assert(std::is_same_v, int>); + static_assert(std::is_same_v, int>); + static_assert(std::is_same_v, int>); - static_assert(std::tuple_size_v == 4, "size4 size wrong"); - static_assert(std::tuple_size_v == 4, "size4 size wrong"); - - static_assert(std::is_same_v, int>, "size4 type wrong"); - static_assert(std::is_same_v, int>, "size4 type wrong"); - static_assert(std::is_same_v, int>, "size4 type wrong"); - static_assert(std::is_same_v, int>, "size4 type wrong"); - - static_assert(std::is_same_v, int>, "const4 type wrong"); - static_assert(std::is_same_v, int>, "const4 type wrong"); - static_assert(std::is_same_v, int>, "const4 type wrong"); - static_assert(std::is_same_v, int>, "const4 type wrong"); + static_assert(std::is_same_v, int>); + static_assert(std::is_same_v, int>); + static_assert(std::is_same_v, int>); + static_assert(std::is_same_v, int>); constexpr static size4 seq4{}; - static_assert(get<0>(seq4) == 9, "size4 element 0 wrong"); - static_assert(get<1>(seq4) == 8, "size4 element 1 wrong"); - static_assert(get<2>(seq4) == 7, "size4 element 2 wrong"); - static_assert(get<3>(seq4) == 2, "size4 element 3 wrong"); + static_assert(get<0>(seq4) == 9); + static_assert(get<1>(seq4) == 8); + static_assert(get<2>(seq4) == 7); + static_assert(get<3>(seq4) == 2); return 0; } From a795b2c0a188198b253f0a1a139afed990002d1a Mon Sep 17 00:00:00 2001 From: Tsche Date: Sun, 9 Nov 2025 06:48:46 +0100 Subject: [PATCH 06/13] remove extraneous space --- .../std/utilities/intseq/intseq.binding/integer_seq.pass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp index fc3f039c1d265..c5baba7a101d9 100644 --- a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp +++ b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp @@ -22,7 +22,7 @@ int main(int, char**) { using empty = std::integer_sequence; static_assert(std::tuple_size_v == 0); static_assert(std::tuple_size_v == 0); - + using size4 = std::integer_sequence; static_assert(std::tuple_size_v == 4); static_assert(std::tuple_size_v == 4); From 2a238dcfb2c148e0c5a15fd8c503a2156cf9664e Mon Sep 17 00:00:00 2001 From: Tsche Date: Sun, 9 Nov 2025 07:36:32 +0100 Subject: [PATCH 07/13] address more review comments --- libcxx/include/utility | 12 ++++++++++++ ...seq.pass.cpp => integer_seq.compile.pass.cpp} | 16 ++++++++++------ .../intseq/intseq.binding/integer_seq.verify.cpp | 16 ++++++++++++---- 3 files changed, 34 insertions(+), 10 deletions(-) rename libcxx/test/std/utilities/intseq/intseq.binding/{integer_seq.pass.cpp => integer_seq.compile.pass.cpp} (78%) diff --git a/libcxx/include/utility b/libcxx/include/utility index bc4eaf6a0cd02..1b19243afca1b 100644 --- a/libcxx/include/utility +++ b/libcxx/include/utility @@ -216,6 +216,18 @@ template template using index_sequence_for = make_index_sequence; +template // C++26 + struct tuple_size>; + +template // C++26 + struct tuple_element>; + +template // C++26 + struct tuple_element>; + +template // C++26 + constexpr T get(integer_sequence) noexcept; + template constexpr T exchange(T& obj, U&& new_value) // constexpr in C++17, noexcept in C++23 noexcept(is_nothrow_move_constructible::value && is_nothrow_assignable::value); diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.compile.pass.cpp similarity index 78% rename from libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp rename to libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.compile.pass.cpp index c5baba7a101d9..4d23f2c03e77b 100644 --- a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.pass.cpp +++ b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.compile.pass.cpp @@ -7,18 +7,24 @@ //===----------------------------------------------------------------------===// // REQUIRES: std-at-least-c++26 + // -// structured binding support for integer_sequence +// template +// struct tuple_size>; +// template +// struct tuple_element>; +// template +// struct tuple_element>; +// template +// constexpr T get(integer_sequence) noexcept; #include #include #include #include -#include "test_macros.h" - -int main(int, char**) { +constexpr void test() { using empty = std::integer_sequence; static_assert(std::tuple_size_v == 0); static_assert(std::tuple_size_v == 0); @@ -42,6 +48,4 @@ int main(int, char**) { static_assert(get<1>(seq4) == 8); static_assert(get<2>(seq4) == 7); static_assert(get<3>(seq4) == 2); - - return 0; } diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp index e25d95fbda32e..3e9d8d7d18dfc 100644 --- a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp +++ b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp @@ -7,17 +7,25 @@ //===----------------------------------------------------------------------===// // REQUIRES: std-at-least-c++26 + // +// template +// struct tuple_element>; +// template +// struct tuple_element>; +// template +// constexpr T get(integer_sequence) noexcept; + // Expect failures for tuple_element and get with empty integer_sequence #include void f() { - using test1 = typename std::tuple_element<0, std::integer_sequence>:: - type; // expected-error-re@*:* {{static assertion failed{{.*}}Index out of bounds in std::tuple_element<> (std::integer_sequence)}} - using test2 = typename std::tuple_element<0, const std::integer_sequence>:: - type; // expected-error-re@*:* {{static assertion failed{{.*}}Index out of bounds in std::tuple_element<> (const std::integer_sequence)}} + // expected-error-re@*:* {{static assertion failed{{.*}}Index out of bounds in std::tuple_element<> (std::integer_sequence)}} + using test1 = std::tuple_element_t<0, std::integer_sequence>; + // expected-error-re@*:* {{static assertion failed{{.*}}Index out of bounds in std::tuple_element<> (const std::integer_sequence)}} + using test2 = std::tuple_element_t<0, const std::integer_sequence>; auto empty = std::integer_sequence(); // expected-error-re@*:* {{static assertion failed{{.*}}Index out of bounds in std::get<> (std::integer_sequence)}} From 49c65945cd9e3de62b48a2743a77c3e7df7c9008 Mon Sep 17 00:00:00 2001 From: Tsche Date: Mon, 10 Nov 2025 08:23:02 +0100 Subject: [PATCH 08/13] add github issue to documentation, sort includes, minor style changes --- libcxx/docs/ReleaseNotes/22.rst | 2 +- libcxx/docs/Status/Cxx2cPapers.csv | 2 +- libcxx/include/__utility/integer_sequence.h | 17 +++++++---------- .../intseq.binding/integer_seq.compile.pass.cpp | 7 +++++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index 685e7a22be28a..08062e4305206 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -48,7 +48,7 @@ Implemented Papers - P2835R7: Expose ``std::atomic_ref``'s object address (`Github `__) - P2944R3: Comparisons for ``reference_wrapper`` (`Github `__) - P3168R2: Give ``std::optional`` Range Support (`Github `__) -- P1789R3: Library Support for Expansion Statements +- P1789R3: Library Support for Expansion Statements (`Github `__) Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index 81604230d651f..c7690f20adba3 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -157,5 +157,5 @@ "`P3552R3 `__","Add a Coroutine Task Type","2025-06 (Sofia)","","","`#148182 `__","" "`P1317R2 `__","Remove return type deduction in ``std::apply``","2025-06 (Sofia)","","","`#148183 `__","" "","","","","","","" -"`P1789R3 `__","Library Support for Expansion Statements","2025-11 (Kona)","|Complete|","22","","" +"`P1789R3 `__","Library Support for Expansion Statements","2025-11 (Kona)","|Complete|","22","`#167268 `__","" "","","","","","","" diff --git a/libcxx/include/__utility/integer_sequence.h b/libcxx/include/__utility/integer_sequence.h index a950431f1b480..c2284b901f3a3 100644 --- a/libcxx/include/__utility/integer_sequence.h +++ b/libcxx/include/__utility/integer_sequence.h @@ -11,13 +11,10 @@ #include <__config> #include <__cstddef/size_t.h> +#include <__tuple/tuple_element.h> +#include <__tuple/tuple_size.h> #include <__type_traits/is_integral.h> -#if _LIBCPP_STD_VER >= 26 -# include <__tuple/tuple_element.h> -# include <__tuple/tuple_size.h> -#endif - #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header #endif @@ -74,23 +71,23 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __for_each_index_sequence(index_sequence<_I # if _LIBCPP_STD_VER >= 26 // structured binding support for integer_sequence -template +template struct tuple_size> : integral_constant {}; -template +template struct tuple_element<_Ip, integer_sequence<_Tp, _Indices...>> { static_assert(_Ip < sizeof...(_Indices), "Index out of bounds in std::tuple_element<> (std::integer_sequence)"); using type _LIBCPP_NODEBUG = _Tp; }; -template +template struct tuple_element<_Ip, const integer_sequence<_Tp, _Indices...>> { static_assert(_Ip < sizeof...(_Indices), "Index out of bounds in std::tuple_element<> (const std::integer_sequence)"); using type _LIBCPP_NODEBUG = _Tp; }; -template -_LIBCPP_HIDE_FROM_ABI constexpr _Tp get(integer_sequence<_Tp, _Indices...>) _NOEXCEPT { +template +_LIBCPP_HIDE_FROM_ABI constexpr _Tp get(integer_sequence<_Tp, _Indices...>) noexcept { static_assert(_Ip < sizeof...(_Indices), "Index out of bounds in std::get<> (std::integer_sequence)"); return _Indices...[_Ip]; } diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.compile.pass.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.compile.pass.cpp index 4d23f2c03e77b..27597ea9b6044 100644 --- a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.compile.pass.cpp +++ b/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.compile.pass.cpp @@ -19,12 +19,13 @@ // template // constexpr T get(integer_sequence) noexcept; +#include #include -#include #include -#include +#include constexpr void test() { + // std::tuple_size_v using empty = std::integer_sequence; static_assert(std::tuple_size_v == 0); static_assert(std::tuple_size_v == 0); @@ -33,6 +34,7 @@ constexpr void test() { static_assert(std::tuple_size_v == 4); static_assert(std::tuple_size_v == 4); + // std::tuple_element_t static_assert(std::is_same_v, int>); static_assert(std::is_same_v, int>); static_assert(std::is_same_v, int>); @@ -43,6 +45,7 @@ constexpr void test() { static_assert(std::is_same_v, int>); static_assert(std::is_same_v, int>); + // std::get constexpr static size4 seq4{}; static_assert(get<0>(seq4) == 9); static_assert(get<1>(seq4) == 8); From 04a6f5fd8efec8479e92c4c09e51b8c9e17e0ad6 Mon Sep 17 00:00:00 2001 From: Tsche Date: Mon, 10 Nov 2025 08:24:02 +0100 Subject: [PATCH 09/13] rename tests --- .../{integer_seq.compile.pass.cpp => binding.compile.pass.cpp} | 0 .../intseq.binding/{integer_seq.verify.cpp => binding.verify.cpp} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename libcxx/test/std/utilities/intseq/intseq.binding/{integer_seq.compile.pass.cpp => binding.compile.pass.cpp} (100%) rename libcxx/test/std/utilities/intseq/intseq.binding/{integer_seq.verify.cpp => binding.verify.cpp} (100%) diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.compile.pass.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/binding.compile.pass.cpp similarity index 100% rename from libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.compile.pass.cpp rename to libcxx/test/std/utilities/intseq/intseq.binding/binding.compile.pass.cpp diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/binding.verify.cpp similarity index 100% rename from libcxx/test/std/utilities/intseq/intseq.binding/integer_seq.verify.cpp rename to libcxx/test/std/utilities/intseq/intseq.binding/binding.verify.cpp From b9781f4983d219b9af4a62ab856ad68615185253 Mon Sep 17 00:00:00 2001 From: Tsche Date: Mon, 10 Nov 2025 15:03:41 +0100 Subject: [PATCH 10/13] rename tests, add noexceptness and return type checks, add structured binding test --- .../structured_binding.pass.cpp | 41 +++++++++++++++++++ ...s.cpp => tuple_interface.compile.pass.cpp} | 6 +++ ....verify.cpp => tuple_interface.verify.cpp} | 0 3 files changed, 47 insertions(+) create mode 100644 libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp rename libcxx/test/std/utilities/intseq/intseq.binding/{binding.compile.pass.cpp => tuple_interface.compile.pass.cpp} (93%) rename libcxx/test/std/utilities/intseq/intseq.binding/{binding.verify.cpp => tuple_interface.verify.cpp} (100%) diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp new file mode 100644 index 0000000000000..d993f38c91f0e --- /dev/null +++ b/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// + +// template +// struct tuple_element>; +// template +// struct tuple_element>; +// template +// constexpr T get(integer_sequence) noexcept; + +#include +#include + +template +void test_structured_bindings() { + constexpr static auto [...empty] = std::make_index_sequence<0>(); + static_assert(sizeof...(empty) == 0); + + constexpr static auto [...size4] = std::make_index_sequence<4>(); + static_assert(sizeof...(size4) == 4); + + // these cannot yet be static_asserts + assert(size4...[0] == 0); + assert(size4...[1] == 1); + assert(size4...[2] == 2); + assert(size4...[3] == 3); +} + +int main(int, char**) { + test_structured_bindings(); + return 0; +} diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/binding.compile.pass.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.compile.pass.cpp similarity index 93% rename from libcxx/test/std/utilities/intseq/intseq.binding/binding.compile.pass.cpp rename to libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.compile.pass.cpp index 27597ea9b6044..84282d987919f 100644 --- a/libcxx/test/std/utilities/intseq/intseq.binding/binding.compile.pass.cpp +++ b/libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.compile.pass.cpp @@ -20,6 +20,7 @@ // constexpr T get(integer_sequence) noexcept; #include +#include #include #include #include @@ -51,4 +52,9 @@ constexpr void test() { static_assert(get<1>(seq4) == 8); static_assert(get<2>(seq4) == 7); static_assert(get<3>(seq4) == 2); + + static_assert(noexcept(get<0>(seq4))); + + constexpr std::same_as decltype(auto) r = get<0>(seq4); + static_assert(r == 9); } diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/binding.verify.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.verify.cpp similarity index 100% rename from libcxx/test/std/utilities/intseq/intseq.binding/binding.verify.cpp rename to libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.verify.cpp From 94909a353d523f84554b057577c412db921269e9 Mon Sep 17 00:00:00 2001 From: Tsche Date: Mon, 10 Nov 2025 15:09:00 +0100 Subject: [PATCH 11/13] fix formatting issues --- .../intseq/intseq.binding/structured_binding.pass.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp index d993f38c91f0e..8c951f2619dcd 100644 --- a/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp +++ b/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp @@ -22,10 +22,10 @@ template void test_structured_bindings() { - constexpr static auto [...empty] = std::make_index_sequence<0>(); + constexpr static auto [... empty] = std::make_index_sequence<0>(); static_assert(sizeof...(empty) == 0); - constexpr static auto [...size4] = std::make_index_sequence<4>(); + constexpr static auto [... size4] = std::make_index_sequence<4>(); static_assert(sizeof...(size4) == 4); // these cannot yet be static_asserts From a7ed5425659967116470b15b950e33adadb689c9 Mon Sep 17 00:00:00 2001 From: Tsche Date: Mon, 10 Nov 2025 18:01:49 +0100 Subject: [PATCH 12/13] drop constexpr --- .../intseq/intseq.binding/structured_binding.pass.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp index 8c951f2619dcd..f0af9af89bc1a 100644 --- a/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp +++ b/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp @@ -22,13 +22,12 @@ template void test_structured_bindings() { - constexpr static auto [... empty] = std::make_index_sequence<0>(); + auto [... empty] = std::make_index_sequence<0>(); static_assert(sizeof...(empty) == 0); - constexpr static auto [... size4] = std::make_index_sequence<4>(); + auto [... size4] = std::make_index_sequence<4>(); static_assert(sizeof...(size4) == 4); - // these cannot yet be static_asserts assert(size4...[0] == 0); assert(size4...[1] == 1); assert(size4...[2] == 2); From 3e3ab8d970ab71fbf5ae8a2801c967e5f04b8f01 Mon Sep 17 00:00:00 2001 From: Tsche Date: Mon, 10 Nov 2025 23:33:42 +0100 Subject: [PATCH 13/13] guard p1061 structured bindings behind FTM, add structured binding test case --- .../intseq.binding/structured_binding.pass.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp index f0af9af89bc1a..4702fbe989cd0 100644 --- a/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp +++ b/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp @@ -20,8 +20,18 @@ #include #include -template void test_structured_bindings() { + auto [elt0, elt1, elt2, elt3] = std::make_index_sequence<4>(); + + assert(elt0 == 0); + assert(elt1 == 1); + assert(elt2 == 2); + assert(elt3 == 3); +} + +#if __cpp_structured_bindings >= 202411L +template +void test_p1061_structured_bindings() { auto [... empty] = std::make_index_sequence<0>(); static_assert(sizeof...(empty) == 0); @@ -33,8 +43,12 @@ void test_structured_bindings() { assert(size4...[2] == 2); assert(size4...[3] == 3); } +#endif int main(int, char**) { test_structured_bindings(); +#if __cpp_structured_bindings >= 202411L + test_p1061_structured_bindings(); +#endif return 0; }