From 617b446176b2014a288ee14a5f4ce0ac1d0283a2 Mon Sep 17 00:00:00 2001 From: Matthias Wippich Date: Fri, 26 Dec 2025 02:07:13 +0100 Subject: [PATCH] [libc++] Implement P1789R3: Library Support for Expansion Statements (#167184) [P1789R3](https://isocpp.org/files/papers/P1789R3.pdf) was accepted for C++26 through LWG motion 14 at the 2025 Kona meeting. This patch implements it, along with tests and documentation changes. Closes #167268 --------- Co-authored-by: Tsche --- libcxx/docs/FeatureTestMacroTable.rst | 2 + libcxx/docs/ReleaseNotes/22.rst | 1 + libcxx/docs/Status/Cxx2cPapers.csv | 2 +- libcxx/include/__utility/integer_sequence.h | 26 +++++++++ libcxx/include/utility | 12 ++++ libcxx/include/version | 5 +- .../utilities/intseq/nodiscard.verify.cpp | 5 ++ .../utility.version.compile.pass.cpp | 4 +- .../version.version.compile.pass.cpp | 4 +- .../structured_binding.pass.cpp | 54 +++++++++++++++++ .../tuple_interface.compile.pass.cpp | 58 +++++++++++++++++++ .../intseq.binding/tuple_interface.verify.cpp | 34 +++++++++++ .../generate_feature_test_macro_components.py | 5 +- 13 files changed, 205 insertions(+), 7 deletions(-) create mode 100644 libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp create mode 100644 libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.compile.pass.cpp create mode 100644 libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.verify.cpp diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 4a82461b8053..32911d0f6444 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 82414e88ac5f..0d1a1fbc00f2 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -53,6 +53,7 @@ Implemented Papers - P3168R2: Give ``std::optional`` Range Support (`Github `__) - P3567R2: ``flat_meow`` Fixes (`Github `__) - P3836R2: Make ``optional`` trivially copyable (`Github `__) +- 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 6a77ec83b61b..9bb5d2bda3d4 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -169,7 +169,7 @@ "`P3819R0 `__","Remove ``evaluation_exception()`` from contract-violation handling for C++26","2025-11 (Kona)","","","`#171280 `__","" "`P3612R1 `__","Harmonize proxy-reference operations (LWG 3638 and 4187)","2025-11 (Kona)","","","`#171281 `__","" "`P3778R0 `__","Fix for ``type_order`` template definition","2025-11 (Kona)","","","`#171284 `__","" -"`P1789R3 `__","Library Support for Expansion Statements","2025-11 (Kona)","","","`#167268 `__","" +"`P1789R3 `__","Library Support for Expansion Statements","2025-11 (Kona)","|Complete|","22","`#167268 `__","" "`P3922R1 `__","Missing deduction guide from ``simd::mask`` to ``simd::vec``","2025-11 (Kona)","","","`#171285 `__","" "`P3878R1 `__","Standard library hardening should not use the 'observe' semantic","2025-11 (Kona)","","","`#171286 `__","" "`P3887R1 `__","Make ``when_all`` a Ronseal Algorithm","2025-11 (Kona)","","","`#171289 `__","" diff --git a/libcxx/include/__utility/integer_sequence.h b/libcxx/include/__utility/integer_sequence.h index 9450d526ed32..a84f572c3339 100644 --- a/libcxx/include/__utility/integer_sequence.h +++ b/libcxx/include/__utility/integer_sequence.h @@ -11,6 +11,8 @@ #include <__config> #include <__cstddef/size_t.h> +#include <__tuple/tuple_element.h> +#include <__tuple/tuple_size.h> #include <__type_traits/is_integral.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -70,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 +// [intseq.binding], structured binding support +template +struct tuple_size> : 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 +[[nodiscard]] _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 >= 26 + # endif // _LIBCPP_STD_VER >= 14 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/utility b/libcxx/include/utility index bc4eaf6a0cd0..1b19243afca1 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/include/version b/libcxx/include/version index f959c0ab227a..ab781466f5ed 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -141,7 +141,8 @@ __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 + 201304L // C++14 __cpp_lib_integral_constant_callable 201304L __cpp_lib_interpolate 201902L __cpp_lib_invoke 201411L @@ -583,6 +584,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/libcxx/utilities/intseq/nodiscard.verify.cpp b/libcxx/test/libcxx/utilities/intseq/nodiscard.verify.cpp index 4ca52ace08dd..811759f37823 100644 --- a/libcxx/test/libcxx/utilities/intseq/nodiscard.verify.cpp +++ b/libcxx/test/libcxx/utilities/intseq/nodiscard.verify.cpp @@ -14,8 +14,13 @@ #include +#include "test_macros.h" + void test() { std::integer_sequence seq; seq.size(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +#if TEST_STD_VER >= 26 + get<0>(seq); // expected-warning {{ignoring return value of function}} +#endif } 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 b882a5df04ae..1d82ef9ec0e8 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 28fae95c1c1f..b3b424a1d77c 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/structured_binding.pass.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp new file mode 100644 index 000000000000..650e0d651ea7 --- /dev/null +++ b/libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +constexpr bool test() { + auto [elt0, elt1, elt2, elt3] = std::make_index_sequence<4>(); + + assert(elt0 == 0); + assert(elt1 == 1); + assert(elt2 == 2); + assert(elt3 == 3); + +// TODO: remove this macro guard once GCC16 is available +#if __cpp_structured_bindings >= 202411L + [] { + auto [... empty] = std::make_index_sequence<0>(); + static_assert(sizeof...(empty) == 0); + + auto [... size4] = std::make_index_sequence<4>(); + static_assert(sizeof...(size4) == 4); + + assert(size4...[0] == 0); + assert(size4...[1] == 1); + assert(size4...[2] == 2); + assert(size4...[3] == 3); + }(); +#endif + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.compile.pass.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.compile.pass.cpp new file mode 100644 index 000000000000..026f20eb8c09 --- /dev/null +++ b/libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.compile.pass.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// 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_size>; +// template +// struct tuple_element>; +// template +// struct tuple_element>; +// template +// constexpr T get(integer_sequence) noexcept; + +#include +#include +#include +#include +#include + +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); + + using size4 = std::integer_sequence; + 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>); + 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::is_same_v, int>); + + // std::get + constexpr static size4 seq4; + constexpr std::same_as decltype(auto) elt0 = get<0>(seq4); + static_assert(elt0 == 9); + static_assert(get<1>(seq4) == 8); + static_assert(get<2>(seq4) == 7); + static_assert(get<3>(seq4) == 2); + + static_assert(noexcept(get<0>(seq4))); +} diff --git a/libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.verify.cpp b/libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.verify.cpp new file mode 100644 index 000000000000..a1c894e98572 --- /dev/null +++ b/libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.verify.cpp @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// 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; + +// Expect failures for tuple_element and get with empty integer_sequence + +#include + +void test() { + // 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>; + + std::integer_sequence empty; + // 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 ff965aaa4ff8..f61d6f991cb1 100644 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -764,7 +764,10 @@ feature_test_macros = [ }, { "name": "__cpp_lib_integer_sequence", - "values": {"c++14": 201304}, + "values": { + "c++14": 201304, + "c++26": 202511, # P1789R3 Library Support for Expansion Statements + }, "headers": ["utility"], }, {