Skip to content
Open
2 changes: 2 additions & 0 deletions libcxx/docs/FeatureTestMacroTable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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``
Expand Down
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes/22.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Implemented Papers
- P2835R7: Expose ``std::atomic_ref``'s object address (`Github <https://llvm.org/PR118377>`__)
- P2944R3: Comparisons for ``reference_wrapper`` (`Github <https://llvm.org/PR105424>`__)
- P3168R2: Give ``std::optional`` Range Support (`Github <https://llvm.org/PR105430>`__)
- P1789R3: Library Support for Expansion Statements (`Github <https://llvm.org/PR167268>`__)

Improvements and New Features
-----------------------------
Expand Down
2 changes: 2 additions & 0 deletions libcxx/docs/Status/Cxx2cPapers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,5 @@
"`P3552R3 <https://wg21.link/P3552R3>`__","Add a Coroutine Task Type","2025-06 (Sofia)","","","`#148182 <https://github.com/llvm/llvm-project/issues/148182>`__",""
"`P1317R2 <https://wg21.link/P1317R2>`__","Remove return type deduction in ``std::apply``","2025-06 (Sofia)","","","`#148183 <https://github.com/llvm/llvm-project/issues/148183>`__",""
"","","","","","",""
"`P1789R3 <https://wg21.link/P1789R3>`__","Library Support for Expansion Statements","2025-11 (Kona)","|Complete|","22","`#167268 <https://github.com/llvm/llvm-project/issues/167268>`__",""
"","","","","","",""
26 changes: 26 additions & 0 deletions libcxx/include/__utility/integer_sequence.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -67,6 +69,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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

Suggested change
// structured binding support for integer_sequence
// [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>
_LIBCPP_HIDE_FROM_ABI constexpr _Tp get(integer_sequence<_Tp, _Indices...>) noexcept {
Copy link
Contributor

@Zingam Zingam Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_LIBCPP_HIDE_FROM_ABI constexpr _Tp get(integer_sequence<_Tp, _Indices...>) noexcept {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp get(integer_sequence<_Tp, _Indices...>) noexcept {

I think we can mark get as [[nodiscard]]. A test should be added to /libcxx/test/libcxx/utilities/intseq/nodiscard.verify.cpp

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
Expand Down
12 changes: 12 additions & 0 deletions libcxx/include/utility
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 4 additions & 1 deletion libcxx/include/version
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -582,6 +583,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Comment on lines +13 to +18
Copy link
Contributor

@Zingam Zingam Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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;
// <describe what's being tested>

I think you should rename this file to "general.pass.cpp".
libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.compile.pass.cpp -> libcxx/test/std/utilities/intseq/intseq.binding/binding.compile.pass.cpp
libcxx/test/std/utilities/intseq/intseq.binding/tuple_interface.verify.cpp ->
libcxx/test/std/utilities/intseq/intseq.binding/binding.verify.cpp
Which is closer to the convention of how test named: the path consists of the sections in the Standard + function names, etc.


#include <cassert>
#include <utility>

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
Copy link
Contributor

@Zingam Zingam Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually guard with a macro TEST_HAS_<FEATURE_NAME> declared in test_macros.h. I'm not sure if it is totally necessary doing it for a single case but you should at least a TODO to remind to remove this check and maybe list the unsupported compilers.

Suggested change
#if __cpp_structured_bindings >= 202411L
// TODO: Remove...

I guess having a TEST_HAS_ macro declared in test_macros.h will be easier to spot and clean-up.

template <typename...>
void test_p1061_structured_bindings() {
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

int main(int, char**) {
test_structured_bindings();
#if __cpp_structured_bindings >= 202411L
test_p1061_structured_bindings();
#endif
return 0;
}
Comment on lines +23 to +54
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should test both constant evaluation and runtime behavior.

Also, it seems simpler to put the test case for structured binding packs into an immediately invoked lambda expression to avoid duplicated macro guards.

Suggested change
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 <typename...>
void test_p1061_structured_bindings() {
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
int main(int, char**) {
test_structured_bindings();
#if __cpp_structured_bindings >= 202411L
test_p1061_structured_bindings();
#endif
return 0;
}
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);
[]<typename...> {
#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;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe just merge the two tests files? WDYT?

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//===----------------------------------------------------------------------===//
//
// 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>

constexpr void test() {
Copy link
Contributor

@Zingam Zingam Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
constexpr void test() {
void test() {

I think this won't be necessary if you declare constexpr locally and won't be extending the test to do the runtime checks..

// 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{};
static_assert(get<0>(seq4) == 9);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
static_assert(get<0>(seq4) == 9);
constexpr std::same_as<int> decltype(auto) r = get<0>(seq4);
static_assert(r == 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)));

constexpr std::same_as<int> decltype(auto) r = get<0>(seq4);
static_assert(r == 9);
Comment on lines +58 to +59
Copy link
Contributor

@Zingam Zingam Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can replace line 51 with this one as they are doing the same test.

Suggested change
constexpr std::same_as<int> decltype(auto) r = get<0>(seq4);
static_assert(r == 9);

}
Original file line number Diff line number Diff line change
@@ -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 f() {
// 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>>;

auto empty = std::integer_sequence<int>();
// 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);
Comment on lines +30 to +33
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: std::integer_sequence<int> empty; or (void)std::get<0>(`std::integer_sequence{});

}
5 changes: 4 additions & 1 deletion libcxx/utils/generate_feature_test_macro_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
},
{
Expand Down
Loading