[libcxx-commits] [libcxx] [libc++][complex] P2819R2 - Add `tuple` protocol to `complex` (PR #79744)

Hristo Hristov via libcxx-commits libcxx-commits at lists.llvm.org
Sun Jan 28 03:48:22 PST 2024


https://github.com/H-G-Hristov created https://github.com/llvm/llvm-project/pull/79744

Implements: P2819R2 <https://wg21.link/P2819R2>
- https://eel.is/c++draft/utilities#concept:tuple-like
- https://eel.is/c++draft/complex.syn
- https://eel.is/c++draft/complex.tuple

>From fd813532dcf0d75b89d4d5d1094b6822dc803092 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 27 Jan 2024 21:12:16 +0200
Subject: [PATCH] [libc++][complex] P2819R2 - Add `tuple` protocol to `complex`

Implements: P2819R2 <https://wg21.link/P2819R2>
- https://eel.is/c++draft/utilities#concept:tuple-like
- https://eel.is/c++draft/complex.syn
- https://eel.is/c++draft/complex.tuple
---
 libcxx/docs/ReleaseNotes/19.rst               |   2 +-
 libcxx/docs/Status/Cxx2cPapers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__fwd/complex.h                |  25 +++
 libcxx/include/__fwd/get.h                    |  17 ++
 libcxx/include/__tuple/tuple_like.h           |   8 +
 libcxx/include/complex                        | 189 ++++++++++++++++++
 libcxx/include/libcxx.imp                     |   1 +
 libcxx/include/module.modulemap.in            |   2 +
 .../tuple/__tuple_like.compile.pass.cpp       |  76 +++++++
 .../complex.number/complex.tuple/get.pass.cpp |  87 ++++++++
 .../complex.tuple/get.verify.cpp              |  60 ++++++
 .../tuple_element.compile.pass.cpp            |  43 ++++
 .../complex.tuple/tuple_element.verify.cpp    |  31 +++
 .../complex.tuple/tuple_size.compile.pass.cpp |  39 ++++
 .../generate_feature_test_macro_components.py |   2 +-
 16 files changed, 582 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/include/__fwd/complex.h
 create mode 100644 libcxx/test/libcxx/utilities/tuple/__tuple_like.compile.pass.cpp
 create mode 100644 libcxx/test/std/numerics/complex.number/complex.tuple/get.pass.cpp
 create mode 100644 libcxx/test/std/numerics/complex.number/complex.tuple/get.verify.cpp
 create mode 100644 libcxx/test/std/numerics/complex.number/complex.tuple/tuple_element.compile.pass.cpp
 create mode 100644 libcxx/test/std/numerics/complex.number/complex.tuple/tuple_element.verify.cpp
 create mode 100644 libcxx/test/std/numerics/complex.number/complex.tuple/tuple_size.compile.pass.cpp

diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index e081bc26d08097c..0b0671ae6c64d5b 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -35,7 +35,7 @@ see the `releases page <https://llvm.org/releases/>`_.
 What's New in Libc++ 19.0.0?
 ==============================
 
-TODO
+P2819R2 - Add ``tuple`` protocol to ``complex``
 
 
 Implemented Papers
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index f80b1f6b663f045..4fc7a23c0902a0b 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -39,7 +39,7 @@
 "`P2868R3 <https://wg21.link/P2868R3>`__","LWG","Remove Deprecated ``std::allocator`` Typedef From C++26","Kona November 2023","|Complete|","18.0",""
 "`P2870R3 <https://wg21.link/P2870R3>`__","LWG","Remove ``basic_string::reserve()`` From C++26","Kona November 2023","|Complete|","18.0",""
 "`P2871R3 <https://wg21.link/P2871R3>`__","LWG","Remove Deprecated Unicode Conversion Facets from C++26","Kona November 2023","|Complete|","18.0",""
-"`P2819R2 <https://wg21.link/P2819R2>`__","LWG","Add tuple protocol to complex","Kona November 2023","","",""
+"`P2819R2 <https://wg21.link/P2819R2>`__","LWG","Add tuple protocol to complex","Kona November 2023","|Complete|","19.0",""
 "`P2937R0 <https://wg21.link/P2937R0>`__","LWG","Freestanding: Remove ``strtok``","Kona November 2023","","",""
 "`P2833R2 <https://wg21.link/P2833R2>`__","LWG","Freestanding Library: inout expected span","Kona November 2023","","",""
 "`P2836R1 <https://wg21.link/P2836R1>`__","LWG","``std::basic_const_iterator`` should follow its underlying type's convertibility","Kona November 2023","","","|DR|"
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index ed721d467e94f4c..05332a3507d07a7 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -426,6 +426,7 @@ set(files
   __functional/weak_result_type.h
   __fwd/array.h
   __fwd/bit_reference.h
+  __fwd/complex.h
   __fwd/fstream.h
   __fwd/get.h
   __fwd/hash.h
diff --git a/libcxx/include/__fwd/complex.h b/libcxx/include/__fwd/complex.h
new file mode 100644
index 000000000000000..1c7be5a5ff75027
--- /dev/null
+++ b/libcxx/include/__fwd/complex.h
@@ -0,0 +1,25 @@
+//===---------------------------------------------------------------------===//
+//
+// 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
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___FWD_COMPLEX_H
+#define _LIBCPP___FWD_COMPLEX_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+class _LIBCPP_TEMPLATE_VIS complex;
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FWD_COMPLEX_H
diff --git a/libcxx/include/__fwd/get.h b/libcxx/include/__fwd/get.h
index e7261b826953d7a..dfd2b5ae8d2bbde 100644
--- a/libcxx/include/__fwd/get.h
+++ b/libcxx/include/__fwd/get.h
@@ -12,6 +12,7 @@
 #include <__concepts/copyable.h>
 #include <__config>
 #include <__fwd/array.h>
+#include <__fwd/complex.h>
 #include <__fwd/pair.h>
 #include <__fwd/subrange.h>
 #include <__fwd/tuple.h>
@@ -76,6 +77,22 @@ template <size_t _Ip, class _Tp, size_t _Size>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const _Tp&& get(const array<_Tp, _Size>&&) _NOEXCEPT;
 #endif
 
+#if _LIBCPP_STD_VER >= 26
+
+template <size_t _Ip, class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp& get(complex<_Tp>&) noexcept;
+
+template <size_t _Ip, class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp&& get(complex<_Tp>&&) noexcept;
+
+template <size_t _Ip, class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr const _Tp& get(const complex<_Tp>&) noexcept;
+
+template <size_t _Ip, class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& get(const complex<_Tp>&&) noexcept;
+
+#endif // _LIBCPP_STD_VER >= 26
+
 #if _LIBCPP_STD_VER >= 20
 
 namespace ranges {
diff --git a/libcxx/include/__tuple/tuple_like.h b/libcxx/include/__tuple/tuple_like.h
index dab395be616b7d2..0a619e7c9986660 100644
--- a/libcxx/include/__tuple/tuple_like.h
+++ b/libcxx/include/__tuple/tuple_like.h
@@ -11,6 +11,7 @@
 
 #include <__config>
 #include <__fwd/array.h>
+#include <__fwd/complex.h>
 #include <__fwd/pair.h>
 #include <__fwd/subrange.h>
 #include <__fwd/tuple.h>
@@ -41,6 +42,13 @@ struct __tuple_like_impl<array<_Tp, _Size> > : true_type {};
 template <class _Ip, class _Sp, ranges::subrange_kind _Kp>
 struct __tuple_like_impl<ranges::subrange<_Ip, _Sp, _Kp> > : true_type {};
 
+#  if _LIBCPP_STD_VER >= 26
+
+template <class _Tp>
+struct __tuple_like_impl<complex<_Tp>> : true_type {};
+
+#  endif
+
 template <class _Tp>
 concept __tuple_like = __tuple_like_impl<remove_cvref_t<_Tp>>::value;
 
diff --git a/libcxx/include/complex b/libcxx/include/complex
index 02b9db8c516db46..bba875bd8a2b540 100644
--- a/libcxx/include/complex
+++ b/libcxx/include/complex
@@ -227,12 +227,38 @@ template<class T> complex<T> sqrt (const complex<T>&);
 template<class T> complex<T> tan (const complex<T>&);
 template<class T> complex<T> tanh (const complex<T>&);
 
+  // [complex.tuple], tuple interface
+  template<class T> struct tuple_size;                             // Since C++26
+  template<size_t I, class T> struct tuple_element;                // Since C++26
+  template<class T> struct tuple_size<complex<T>>;                 // Since C++26
+  template<size_t I, class T> struct tuple_element<I, complex<T>>; // Since C++26
+  template<size_t I, class T>
+    constexpr T& get(complex<T>&) noexcept;                        // Since C++26
+  template<size_t I, class T>
+    constexpr T&& get(complex<T>&&) noexcept;                      // Since C++26
+  template<size_t I, class T>
+    constexpr const T& get(const complex<T>&) noexcept;            // Since C++26
+  template<size_t I, class T>
+    constexpr const T&& get(const complex<T>&&) noexcept;          // Since C++26
+
+  // [complex.literals], complex literals
+  inline namespace literals {
+  inline namespace complex_literals {
+    constexpr complex<long double> operator""il(long double);        // Since C++14
+    constexpr complex<long double> operator""il(unsigned long long); // Since C++14
+    constexpr complex<double> operator""i(long double);              // Since C++14
+    constexpr complex<double> operator""i(unsigned long long);       // Since C++14
+    constexpr complex<float> operator""if(long double);              // Since C++14
+    constexpr complex<float> operator""if(unsigned long long);       // Since C++14
+  }
+  }
 }  // std
 
 */
 
 #include <__assert> // all public C++ headers provide the assertion handler
 #include <__config>
+#include <__utility/move.h>
 #include <cmath>
 #include <version>
 
@@ -331,6 +357,20 @@ public:
     *this = *this / complex(__c.real(), __c.imag());
     return *this;
   }
+
+#if _LIBCPP_STD_VER >= 26
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr _Xp& get(complex<_Xp>&) noexcept;
+
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr _Xp&& get(complex<_Xp>&&) noexcept;
+
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr const _Xp& get(const complex<_Xp>&) noexcept;
+
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr const _Xp&& get(const complex<_Xp>&&) noexcept;
+#endif
 };
 
 template <>
@@ -408,6 +448,20 @@ public:
     *this = *this / complex(__c.real(), __c.imag());
     return *this;
   }
+
+#if _LIBCPP_STD_VER >= 26
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr _Xp& get(complex<_Xp>&) noexcept;
+
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr _Xp&& get(complex<_Xp>&&) noexcept;
+
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr const _Xp& get(const complex<_Xp>&) noexcept;
+
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr const _Xp&& get(const complex<_Xp>&&) noexcept;
+#endif
 };
 
 template <>
@@ -480,6 +534,20 @@ public:
     *this = *this / complex(__c.real(), __c.imag());
     return *this;
   }
+
+#if _LIBCPP_STD_VER >= 26
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr _Xp& get(complex<_Xp>&) noexcept;
+
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr _Xp&& get(complex<_Xp>&&) noexcept;
+
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr const _Xp& get(const complex<_Xp>&) noexcept;
+
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr const _Xp&& get(const complex<_Xp>&&) noexcept;
+#endif
 };
 
 template <>
@@ -553,6 +621,20 @@ public:
     *this = *this / complex(__c.real(), __c.imag());
     return *this;
   }
+
+#if _LIBCPP_STD_VER >= 26
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr _Xp& get(complex<_Xp>&) noexcept;
+
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr _Xp&& get(complex<_Xp>&&) noexcept;
+
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr const _Xp& get(const complex<_Xp>&) noexcept;
+
+  template <size_t _Ip, class _Xp>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr const _Xp&& get(const complex<_Xp>&&) noexcept;
+#endif
 };
 
 inline _LIBCPP_CONSTEXPR complex<float>::complex(const complex<double>& __c) : __re_(__c.real()), __im_(__c.imag()) {}
@@ -1352,6 +1434,113 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const complex<_Tp>& __x) {
 }
 #endif // !_LIBCPP_HAS_NO_LOCALIZATION
 
+#if _LIBCPP_STD_VER >= 26
+
+// [complex.tuple], tuple interface
+
+template <class _Tp>
+struct tuple_size;
+
+template <class _Tp>
+struct tuple_size<complex<_Tp>> : integral_constant<size_t, 2> {};
+
+template <size_t _Ip, class _Tp>
+struct tuple_element;
+
+template <size_t _Ip, class _Tp>
+struct tuple_element<_Ip, complex<_Tp>> {
+  static_assert(_Ip < 2, "Index value is out of range.");
+  using type = _Tp;
+};
+
+template <size_t _Ip, class _Xp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Xp& get(complex<_Xp>& __z) noexcept {
+  static_assert(_Ip < 2, "Index value is out of range.");
+  if constexpr (_Ip == 0) {
+    return __z.__re_;
+  } else {
+    return __z.__im_;
+  }
+}
+
+template <size_t _Ip, class _Xp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Xp&& get(complex<_Xp>&& __z) noexcept {
+  static_assert(_Ip < 2, "Index value is out of range.");
+  if constexpr (_Ip == 0) {
+    return std::move(__z.__re_);
+  } else {
+    return std::move(__z.__im_);
+  }
+}
+
+template <size_t _Ip, class _Xp>
+_LIBCPP_HIDE_FROM_ABI constexpr const _Xp& get(const complex<_Xp>& __z) noexcept {
+  static_assert(_Ip < 2, "Index value is out of range.");
+  if constexpr (_Ip == 0) {
+    return __z.__re_;
+  } else {
+    return __z.__im_;
+  }
+}
+
+template <size_t _Ip, class _Xp>
+_LIBCPP_HIDE_FROM_ABI constexpr const _Xp&& get(const complex<_Xp>&& __z) noexcept {
+  static_assert(_Ip < 2, "Index value is out of range.");
+  if constexpr (_Ip == 0) {
+    return std::move(__z.__re_);
+  } else {
+    return std::move(__z.__im_);
+  }
+}
+
+// template <size_t _Ip, class _Tp>
+// _LIBCPP_HIDE_FROM_ABI constexpr _Tp& get(complex<_Tp>& __z) noexcept {
+//   static_assert(_Ip < 2, "Index value is out of range.");
+//   (void) __z;
+//     constexpr static auto r = 27;
+//     constexpr static auto i = 28;
+//   if constexpr (_Ip == 0) {
+//     return static_cast<_Tp&>(r);
+//   } else {
+//     return  static_cast<_Tp&>(i);
+//   }
+// }
+
+// template <size_t _Ip, class _Tp>
+// _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& get(complex<_Tp>&& __z) noexcept {
+//   static_assert(_Ip < 2, "Index value is out of range.");
+//   (void) __z;
+//   if constexpr (_Ip == 0) {
+//     return 27;
+//   } else {
+//     return 28;
+//   }
+// }
+
+// template <size_t _Ip, class _Tp>
+// _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& get(const complex<_Tp>& __z) noexcept {
+//   static_assert(_Ip < 2, "Index value is out of range.");
+//   (void) __z;
+//   if constexpr (_Ip == 0) {
+//     return 27;
+//   } else {
+//     return 28;
+//   }
+// }
+
+// template <size_t _Ip, class _Tp>
+// _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& get(const complex<_Tp>&& __z) noexcept {
+//   static_assert(_Ip < 2, "Index value is out of range.");
+//   (void) __z;
+//   if constexpr (_Ip == 0) {
+//     return 27;
+//   } else {
+//     return 28;
+//   }
+// }
+
+#endif // _LIBCPP_STD_VER >= 26
+
 #if _LIBCPP_STD_VER >= 14
 // Literal suffix for complex number literals [complex.literals]
 inline namespace literals {
diff --git a/libcxx/include/libcxx.imp b/libcxx/include/libcxx.imp
index 45fa4a9541917f9..4a6ca1c330280ff 100644
--- a/libcxx/include/libcxx.imp
+++ b/libcxx/include/libcxx.imp
@@ -421,6 +421,7 @@
   { include: [ "<__fwd/array.h>", "private", "<array>", "public" ] },
   { include: [ "<__fwd/bit_reference.h>", "private", "<bitset>", "public" ] },
   { include: [ "<__fwd/bit_reference.h>", "private", "<vector>", "public" ] },
+  { include: [ "<__fwd/complex.h>", "private", "<complex>", "public" ] },
   { include: [ "<__fwd/fstream.h>", "private", "<fstream>", "public" ] },
   { include: [ "<__fwd/hash.h>", "private", "<functional>", "public" ] },
   { include: [ "<__fwd/ios.h>", "private", "<ios>", "public" ] },
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 194a74a1e07b145..a5aae481cff54e9 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1197,6 +1197,8 @@ module std_private_compare_synth_three_way                [system] { header "__c
 module std_private_compare_three_way_comparable           [system] { header "__compare/three_way_comparable.h" }
 module std_private_compare_weak_order                     [system] { header "__compare/weak_order.h" }
 
+module std_private_complex_complex_fwd [system] { header "__fwd/complex.h" }
+
 module std_private_concepts_arithmetic            [system] { header "__concepts/arithmetic.h" }
 module std_private_concepts_assignable            [system] { header "__concepts/assignable.h" }
 module std_private_concepts_boolean_testable      [system] { header "__concepts/boolean_testable.h" }
diff --git a/libcxx/test/libcxx/utilities/tuple/__tuple_like.compile.pass.cpp b/libcxx/test/libcxx/utilities/tuple/__tuple_like.compile.pass.cpp
new file mode 100644
index 000000000000000..9f04f2cc04304bf
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/tuple/__tuple_like.compile.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <tuple>
+
+#include <array>
+#include <complex>
+#include <ranges>
+#include <tuple>
+
+#include "test_iterators.h"
+
+static_assert(!std::__tuple_like<int>);
+
+static_assert(std::__tuple_like<std::array<int, 0>>);
+static_assert(std::__tuple_like<std::array<int, 1>>);
+static_assert(std::__tuple_like<std::array<int, 2>>);
+static_assert(std::__tuple_like<std::array<int, 2728>>);
+
+#if _LIBCPP_STD_VER >= 26
+static_assert(std::__tuple_like<std::complex<float>>);
+static_assert(std::__tuple_like<std::complex<double>>);
+static_assert(std::__tuple_like<std::complex<long double>>);
+#endif
+
+static_assert(std::__tuple_like<std::pair<int, float>>);
+
+static_assert(std::__tuple_like<std::tuple<int>>);
+static_assert(std::__tuple_like<std::tuple<int, float>>);
+static_assert(std::__tuple_like<std::tuple<int, float, double>>);
+
+using FI = forward_iterator<int*>;
+static_assert(std::__tuple_like<std::ranges::subrange<FI, FI, std::ranges::subrange_kind::sized>>);
+static_assert(std::__tuple_like<std::ranges::subrange<FI, FI, std::ranges::subrange_kind::unsized>>);
+static_assert(std::__tuple_like<std::ranges::subrange<int*, int*, std::ranges::subrange_kind::sized>>);
+static_assert(std::__tuple_like<std::ranges::subrange<int*, std::nullptr_t, std::ranges::subrange_kind::unsized>>);
+
+template <typename Iter>
+void test_subrange_sized() {
+  static_assert(std::__tuple_like<std::ranges::subrange<Iter, Iter, std::ranges::subrange_kind::sized>>);
+}
+
+template <typename Iter>
+void test_subrange_unsized() {
+  static_assert(std::__tuple_like<std::ranges::subrange<Iter, Iter, std::ranges::subrange_kind::unsized>>);
+}
+
+void test() {
+  test_subrange_sized<forward_iterator<int*>>();
+  test_subrange_sized<bidirectional_iterator<int*>>();
+  test_subrange_sized<random_access_iterator<int*>>();
+  test_subrange_sized<contiguous_iterator<int*>>();
+  test_subrange_sized<int*>();
+
+  test_subrange_sized<forward_iterator<int const*>>();
+  test_subrange_sized<bidirectional_iterator<int const*>>();
+  test_subrange_sized<random_access_iterator<int const*>>();
+  test_subrange_sized<contiguous_iterator<int const*>>();
+  test_subrange_sized<int const*>();
+
+  test_subrange_unsized<forward_iterator<int*>>();
+  test_subrange_unsized<bidirectional_iterator<int*>>();
+  static_assert(std::__tuple_like<std::ranges::subrange<int*, std::nullptr_t, std::ranges::subrange_kind::unsized>>);
+
+  test_subrange_unsized<forward_iterator<int const*>>();
+  test_subrange_unsized<bidirectional_iterator<int const*>>();
+  static_assert(
+      std::__tuple_like<std::ranges::subrange<const int*, std::nullptr_t, std::ranges::subrange_kind::unsized>>);
+}
diff --git a/libcxx/test/std/numerics/complex.number/complex.tuple/get.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.tuple/get.pass.cpp
new file mode 100644
index 000000000000000..0ada5c71777a72d
--- /dev/null
+++ b/libcxx/test/std/numerics/complex.number/complex.tuple/get.pass.cpp
@@ -0,0 +1,87 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <complex>
+
+//   template<size_t I, class T>
+//     constexpr T& get(complex<T>&) noexcept;
+//   template<size_t I, class T>
+//     constexpr T&& get(complex<T>&&) noexcept;
+//   template<size_t I, class T>
+//     constexpr const T& get(const complex<T>&) noexcept;
+//   template<size_t I, class T>
+//     constexpr const T&& get(const complex<T>&&) noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <complex>
+#include <utility>
+
+template <typename T>
+constexpr void test() {
+  // &
+  {
+    std::complex<T> c{T{27}, T{28}};
+
+    std::same_as<T&> decltype(auto) r = get<0>(c);
+    static_assert(noexcept(get<0>(c)));
+    assert(r == T{27});
+    std::same_as<T&> decltype(auto) i = get<1>(c);
+    static_assert(noexcept(get<1>(c)));
+    assert(i == T{28});
+  }
+  //  &&
+  {
+    std::complex<T> c{T{27}, T{28}};
+
+    std::same_as<T&&> decltype(auto) r = get<0>(std::move(c));
+    static_assert(noexcept(get<0>(c)));
+    assert(r == T{27});
+    std::same_as<T&&> decltype(auto) i = get<1>(std::move(c));
+    assert(i == T{28});
+  }
+  // const &
+  {
+    const std::complex<T> c{T{27}, T{28}};
+
+    std::same_as<const T&> decltype(auto) r = get<0>(c);
+    static_assert(noexcept(get<0>(c)));
+    assert(r == T{27});
+    std::same_as<const T&> decltype(auto) i = get<1>(c);
+    static_assert(noexcept(get<1>(c)));
+    assert(i == T{28});
+  }
+  //  const &&
+  {
+    const std::complex<T> c{T{27}, T{28}};
+
+    std::same_as<const T&&> decltype(auto) r = get<0>(std::move(c));
+    static_assert(noexcept(get<0>(c)));
+    assert(r == T{27});
+    std::same_as<const T&&> decltype(auto) i = get<1>(std::move(c));
+    static_assert(noexcept(get<1>(c)));
+    assert(i == T{28});
+  }
+}
+
+constexpr bool test() {
+  test<float>();
+  test<double>();
+  test<long double>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/complex.number/complex.tuple/get.verify.cpp b/libcxx/test/std/numerics/complex.number/complex.tuple/get.verify.cpp
new file mode 100644
index 000000000000000..6a6e3a968d50f79
--- /dev/null
+++ b/libcxx/test/std/numerics/complex.number/complex.tuple/get.verify.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <complex>
+
+//   template<size_t I, class T>
+//     constexpr T& get(complex<T>&) noexcept;
+//   template<size_t I, class T>
+//     constexpr T&& get(complex<T>&&) noexcept;
+//   template<size_t I, class T>
+//     constexpr const T& get(const complex<T>&) noexcept;
+//   template<size_t I, class T>
+//     constexpr const T&& get(const complex<T>&&) noexcept;
+
+#include <cassert>
+#include <complex>
+#include <utility>
+
+template <typename T>
+void test() {
+  using C = std::complex<T>;
+
+  // &
+  {
+    C c{T{27}, T{28}};
+    // expected-error-re@*:* 3{{static assertion failed {{.*}}Index value is out of range.}}
+    std::get<3>(c);
+  }
+  // &&
+  {
+    C c{T{27}, T{28}};
+    // expected-error-re@*:* 3 {{static assertion failed {{.*}}Index value is out of range.}}
+    std::get<3>(std::move(c));
+  }
+  // const &
+  {
+    const C c{T{27}, T{28}};
+    // expected-error-re@*:* 3 {{static assertion failed {{.*}}Index value is out of range.}}
+    std::get<3>(c);
+  }
+  // const &&
+  {
+    const C c{T{27}, T{28}};
+    // expected-error-re@*:* 3 {{static assertion failed {{.*}}Index value is out of range.}}
+    std::get<3>(std::move(c));
+  }
+}
+
+void test() {
+  test<float>();
+  test<double>();
+  test<long double>();
+}
diff --git a/libcxx/test/std/numerics/complex.number/complex.tuple/tuple_element.compile.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.tuple/tuple_element.compile.pass.cpp
new file mode 100644
index 000000000000000..896af798efa2050
--- /dev/null
+++ b/libcxx/test/std/numerics/complex.number/complex.tuple/tuple_element.compile.pass.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <complex>
+
+//   template<size_t I, class T> struct tuple_element;
+
+#include <cassert>
+#include <concepts>
+#include <complex>
+
+template <size_t I, typename C>
+concept HasTupleElement = requires { std::tuple_element<I, C>{}; };
+
+struct SomeObject {};
+
+static_assert(!HasTupleElement<0, SomeObject>);
+static_assert(!HasTupleElement<1, SomeObject>);
+static_assert(!HasTupleElement<3, SomeObject>);
+
+template <typename T>
+void test() {
+  using C = std::complex<T>;
+
+  static_assert(HasTupleElement<0, C>);
+  static_assert(HasTupleElement<1, C>);
+
+  static_assert(std::same_as<typename std::tuple_element<0, C>::type, T>);
+  static_assert(std::same_as<typename std::tuple_element<1, C>::type, T>);
+}
+
+void test() {
+  test<float>();
+  test<double>();
+  test<long double>();
+}
diff --git a/libcxx/test/std/numerics/complex.number/complex.tuple/tuple_element.verify.cpp b/libcxx/test/std/numerics/complex.number/complex.tuple/tuple_element.verify.cpp
new file mode 100644
index 000000000000000..3fab9e94b0005f0
--- /dev/null
+++ b/libcxx/test/std/numerics/complex.number/complex.tuple/tuple_element.verify.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <complex>
+
+//   template<size_t I, class T> struct tuple_element;
+
+#include <cassert>
+#include <concepts>
+#include <complex>
+
+template <typename T>
+void test() {
+  using C = std::complex<T>;
+
+  // expected-error-re@*:* 3 {{static assertion failed {{.*}}Index value is out of range.}}
+  [[maybe_unused]] std::tuple_element<3, C> te{};
+}
+
+void test() {
+  test<float>();
+  test<double>();
+  test<long double>();
+}
diff --git a/libcxx/test/std/numerics/complex.number/complex.tuple/tuple_size.compile.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.tuple/tuple_size.compile.pass.cpp
new file mode 100644
index 000000000000000..681268044551107
--- /dev/null
+++ b/libcxx/test/std/numerics/complex.number/complex.tuple/tuple_size.compile.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <complex>
+
+//   template<class T> struct tuple_size;
+
+#include <cassert>
+#include <concepts>
+#include <complex>
+
+template <typename C>
+concept HasTupleSize = requires { std::tuple_size<C>{}; };
+
+struct SomeObject {};
+
+static_assert(!HasTupleSize<SomeObject>);
+
+template <typename T>
+void test() {
+  using C = std::complex<T>;
+
+  static_assert(HasTupleSize<C>);
+  static_assert(std::same_as<typename std::tuple_size<C>::value_type, size_t>);
+  static_assert(std::tuple_size<C>() == 2);
+}
+
+void test() {
+  test<float>();
+  test<double>();
+  test<long double>();
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index b7d95d451f21376..29352447b5a986b 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1222,7 +1222,7 @@ def add_version_header(tc):
             "name": "__cpp_lib_tuple_like",
             "values": {
                 "c++23": 202207,  # P2165R4 Compatibility between tuple, pair and tuple-like objects
-                "c++26": 202311,  # P2819R2 Add tuple protocol to complex
+                "c++26": 202311,  # P2819R2 Add tuple protocol to complex (implemented)
             },
             "headers": ["map", "tuple", "unordered_map", "utility"],
             "unimplemented": True,



More information about the libcxx-commits mailing list