[libcxx-commits] [libcxx] 617b446 - [libc++] Implement P1789R3: Library Support for Expansion Statements (#167184)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Dec 25 17:07:18 PST 2025


Author: Matthias Wippich
Date: 2025-12-26T09:07:13+08:00
New Revision: 617b446176b2014a288ee14a5f4ce0ac1d0283a2

URL: https://github.com/llvm/llvm-project/commit/617b446176b2014a288ee14a5f4ce0ac1d0283a2
DIFF: https://github.com/llvm/llvm-project/commit/617b446176b2014a288ee14a5f4ce0ac1d0283a2.diff

LOG: [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 <che at pydong.org>

Added: 
    libcxx/test/std/utilities/intseq/intseq.binding/structured_binding.pass.cpp
    libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.compile.pass.cpp
    libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.verify.cpp

Modified: 
    libcxx/docs/FeatureTestMacroTable.rst
    libcxx/docs/ReleaseNotes/22.rst
    libcxx/docs/Status/Cxx2cPapers.csv
    libcxx/include/__utility/integer_sequence.h
    libcxx/include/utility
    libcxx/include/version
    libcxx/test/libcxx/utilities/intseq/nodiscard.verify.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
    libcxx/utils/generate_feature_test_macro_components.py

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 4a82461b80534..32911d0f64449 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 82414e88ac5fb..0d1a1fbc00f2c 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 <https://llvm.org/PR105430>`__)
 - P3567R2: ``flat_meow`` Fixes (`Github <https://llvm.org/PR162022>`__)
 - P3836R2: Make ``optional<T&>`` trivially copyable (`Github <https://llvm.org/PR171275>`__)
+- P1789R3: Library Support for Expansion Statements (`Github <https://llvm.org/PR167184>`__)
 
 Improvements and New Features
 -----------------------------

diff  --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 6a77ec83b61b8..9bb5d2bda3d4d 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -169,7 +169,7 @@
 "`P3819R0 <https://wg21.link/P3819R0>`__","Remove ``evaluation_exception()`` from contract-violation handling for C++26","2025-11 (Kona)","","","`#171280 <https://github.com/llvm/llvm-project/issues/171280>`__",""
 "`P3612R1 <https://wg21.link/P3612R1>`__","Harmonize proxy-reference operations (LWG 3638 and 4187)","2025-11 (Kona)","","","`#171281 <https://github.com/llvm/llvm-project/issues/171281>`__",""
 "`P3778R0 <https://wg21.link/P3778R0>`__","Fix for ``type_order`` template definition","2025-11 (Kona)","","","`#171284 <https://github.com/llvm/llvm-project/issues/171284>`__",""
-"`P1789R3 <https://wg21.link/P1789R3>`__","Library Support for Expansion Statements","2025-11 (Kona)","","","`#167268 <https://github.com/llvm/llvm-project/issues/167268>`__",""
+"`P1789R3 <https://wg21.link/P1789R3>`__","Library Support for Expansion Statements","2025-11 (Kona)","|Complete|","22","`#167268 <https://github.com/llvm/llvm-project/issues/167268>`__",""
 "`P3922R1 <https://wg21.link/P3922R1>`__","Missing deduction guide from ``simd::mask`` to ``simd::vec``","2025-11 (Kona)","","","`#171285 <https://github.com/llvm/llvm-project/issues/171285>`__",""
 "`P3878R1 <https://wg21.link/P3878R1>`__","Standard library hardening should not use the 'observe' semantic","2025-11 (Kona)","","","`#171286 <https://github.com/llvm/llvm-project/issues/171286>`__",""
 "`P3887R1 <https://wg21.link/P3887R1>`__","Make ``when_all`` a Ronseal Algorithm","2025-11 (Kona)","","","`#171289 <https://github.com/llvm/llvm-project/issues/171289>`__",""

diff  --git a/libcxx/include/__utility/integer_sequence.h b/libcxx/include/__utility/integer_sequence.h
index 9450d526ed32c..a84f572c3339c 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 <class _Tp, _Tp... _Indices>
+struct tuple_size<integer_sequence<_Tp, _Indices...>> : integral_constant<size_t, sizeof...(_Indices)> {};
+
+template <size_t _Ip, class _Tp, _Tp... _Indices>
+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 <size_t _Ip, class _Tp, _Tp... _Indices>
+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 <size_t _Ip, class _Tp, _Tp... _Indices>
+[[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 bc4eaf6a0cd02..1b19243afca1b 100644
--- a/libcxx/include/utility
+++ b/libcxx/include/utility
@@ -216,6 +216,18 @@ template<size_t N>
 template<class... T>
   using index_sequence_for = make_index_sequence<sizeof...(T)>;
 
+template<class T, T... Values>                                                  // C++26
+  struct tuple_size<integer_sequence<T, Values...>>;
+
+template<size_t I, class T, T... Values>                                        // C++26
+  struct tuple_element<I, integer_sequence<T, Values...>>;
+
+template<size_t I, class T, T... Values>                                        // C++26
+  struct tuple_element<I, const integer_sequence<T, Values...>>;
+
+template<size_t I, class T, T... Values>                                        // C++26
+  constexpr T get(integer_sequence<T, Values...>) noexcept;
+
 template<class T, class U=T>
     constexpr T exchange(T& obj, U&& new_value)                                 // constexpr in C++17, noexcept in C++23
       noexcept(is_nothrow_move_constructible<T>::value && is_nothrow_assignable<T&, U>::value);

diff  --git a/libcxx/include/version b/libcxx/include/version
index f959c0ab227ac..ab781466f5ed5 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -141,7 +141,8 @@ __cpp_lib_incomplete_container_elements                 201505L <forward_list> <
 __cpp_lib_inplace_vector                                202406L <inplace_vector>
 __cpp_lib_int_pow2                                      202002L <bit>
 __cpp_lib_integer_comparison_functions                  202002L <utility>
-__cpp_lib_integer_sequence                              201304L <utility>
+__cpp_lib_integer_sequence                              202511L <utility>
+                                                        201304L // C++14
 __cpp_lib_integral_constant_callable                    201304L <type_traits>
 __cpp_lib_interpolate                                   201902L <cmath> <numeric>
 __cpp_lib_invoke                                        201411L <functional>
@@ -583,6 +584,8 @@ __cpp_lib_void_t                                        201411L <type_traits>
 // # 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 4ca52ace08dd3..811759f37823c 100644
--- a/libcxx/test/libcxx/utilities/intseq/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/utilities/intseq/nodiscard.verify.cpp
@@ -14,8 +14,13 @@
 
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   std::integer_sequence<int, 49, 82, 94> 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 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 28fae95c1c1f7..b3b424a1d77ce 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 0000000000000..650e0d651ea7d
--- /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
+
+// <utility>
+
+// template<size_t I, class T, T... Values>
+//   struct tuple_element<I, integer_sequence<T, Values...>>;
+// template<size_t I, class T, T... Values>
+//   struct tuple_element<I, const integer_sequence<T, Values...>>;
+// template<size_t I, class T, T... Values>
+//   constexpr T get(integer_sequence<T, Values...>) noexcept;
+
+#include <cassert>
+#include <utility>
+
+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
+  []<typename...> {
+    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 0000000000000..026f20eb8c09f
--- /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
+
+// <utility>
+
+// template<class T, T... Values>
+//   struct tuple_size<integer_sequence<T, Values...>>;
+// template<size_t I, class T, T... Values>
+//   struct tuple_element<I, integer_sequence<T, Values...>>;
+// template<size_t I, class T, T... Values>
+//   struct tuple_element<I, const integer_sequence<T, Values...>>;
+// template<size_t I, class T, T... Values>
+//   constexpr T get(integer_sequence<T, Values...>) noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+void test() {
+  // std::tuple_size_v
+  using empty = std::integer_sequence<int>;
+  static_assert(std::tuple_size_v<empty> == 0);
+  static_assert(std::tuple_size_v<const empty> == 0);
+
+  using size4 = std::integer_sequence<int, 9, 8, 7, 2>;
+  static_assert(std::tuple_size_v<size4> == 4);
+  static_assert(std::tuple_size_v<const size4> == 4);
+
+  // std::tuple_element_t
+  static_assert(std::is_same_v<std::tuple_element_t<0, size4>, int>);
+  static_assert(std::is_same_v<std::tuple_element_t<1, size4>, int>);
+  static_assert(std::is_same_v<std::tuple_element_t<2, size4>, int>);
+  static_assert(std::is_same_v<std::tuple_element_t<3, size4>, int>);
+
+  static_assert(std::is_same_v<std::tuple_element_t<0, const size4>, int>);
+  static_assert(std::is_same_v<std::tuple_element_t<1, const size4>, int>);
+  static_assert(std::is_same_v<std::tuple_element_t<2, const size4>, int>);
+  static_assert(std::is_same_v<std::tuple_element_t<3, const size4>, int>);
+
+  // std::get
+  constexpr static size4 seq4;
+  constexpr std::same_as<int> 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 0000000000000..a1c894e985729
--- /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
+
+// <utility>
+
+// template<size_t I, class T, T... Values>
+//   struct tuple_element<I, integer_sequence<T, Values...>>;
+// template<size_t I, class T, T... Values>
+//   struct tuple_element<I, const integer_sequence<T, Values...>>;
+// template<size_t I, class T, T... Values>
+//   constexpr T get(integer_sequence<T, Values...>) noexcept;
+
+// Expect failures for tuple_element and get with empty integer_sequence
+
+#include <utility>
+
+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<int>>;
+  // 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<int>>;
+
+  std::integer_sequence<int> 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 ff965aaa4ff89..f61d6f991cb15 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"],
         },
         {


        


More information about the libcxx-commits mailing list